<?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: Russell 👨🏾‍💻</title>
    <description>The latest articles on DEV Community by Russell 👨🏾‍💻 (@_russell).</description>
    <link>https://dev.to/_russell</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%2F920141%2Fafab87c8-55e4-49aa-8c2d-e88fef2554c3.jpg</url>
      <title>DEV Community: Russell 👨🏾‍💻</title>
      <link>https://dev.to/_russell</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/_russell"/>
    <language>en</language>
    <item>
      <title>I Built the Open Source “Microsoft Edge Drop” Replacement Using Cloudflare R2 + Turso</title>
      <dc:creator>Russell 👨🏾‍💻</dc:creator>
      <pubDate>Sun, 22 Feb 2026 20:58:18 +0000</pubDate>
      <link>https://dev.to/_russell/i-built-a-self-hosted-microsoft-edge-drop-replacement-using-cloudflare-r2-turso-2gg0</link>
      <guid>https://dev.to/_russell/i-built-a-self-hosted-microsoft-edge-drop-replacement-using-cloudflare-r2-turso-2gg0</guid>
      <description>&lt;p&gt;I recently built &lt;strong&gt;Cliff Drop&lt;/strong&gt;, a self-hosted, cross-device text and file sharing app inspired by Microsoft Edge Drop.&lt;/p&gt;

&lt;p&gt;But the interesting part isn’t the UI.&lt;/p&gt;

&lt;p&gt;It’s the storage architecture.&lt;/p&gt;

&lt;p&gt;Instead of going traditional:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Monolithic server&lt;/li&gt;
&lt;li&gt;❌ Local disk storage&lt;/li&gt;
&lt;li&gt;❌ Single-instance SQLite&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I split concerns properly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data =&amp;gt; Turso (Edge-hosted SQLite)
&lt;/h3&gt;

&lt;p&gt;For structured data (messages, metadata, auth):&lt;/p&gt;

&lt;p&gt;I used &lt;strong&gt;Turso (libSQL)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;• Edge-hosted SQLite&lt;br&gt;
• Low-latency global reads&lt;br&gt;
• Familiar SQL&lt;br&gt;
• Zero heavy database ops&lt;br&gt;
• Works perfectly with Drizzle ORM&lt;/p&gt;

&lt;p&gt;It gives you SQLite simplicity with distributed database characteristics.&lt;/p&gt;

&lt;p&gt;No managing Postgres clusters.&lt;br&gt;
No complex replication configs.&lt;/p&gt;

&lt;p&gt;Just SQL at the edge.&lt;/p&gt;

&lt;h3&gt;
  
  
  ☁️ File Storage =&amp;gt; Cloudflare R2 (S3-Compatible)
&lt;/h3&gt;

&lt;p&gt;For file uploads and images:&lt;/p&gt;

&lt;p&gt;I used &lt;strong&gt;Cloudflare R2&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Why R2?&lt;/p&gt;

&lt;p&gt;• S3-compatible API&lt;br&gt;
• Zero egress fees&lt;br&gt;
• Object storage at scale&lt;br&gt;
• Works seamlessly with Node.js&lt;br&gt;
• Easy local fallback option&lt;/p&gt;

&lt;p&gt;That means I can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store large file uploads&lt;/li&gt;
&lt;li&gt;Generate thumbnails&lt;/li&gt;
&lt;li&gt;Serve files globally&lt;/li&gt;
&lt;li&gt;Avoid bandwidth surprises&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All without vendor lock-in, since it’s S3-compatible.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚡ Real-Time + Secure
&lt;/h3&gt;

&lt;p&gt;On top of that:&lt;/p&gt;

&lt;p&gt;• Native WebSockets for real-time sync&lt;br&gt;
• Argon2id password hashing&lt;br&gt;
• HTTP-only cookies&lt;br&gt;
• CSRF protection&lt;br&gt;
• CSP headers&lt;br&gt;
• Secure file handling&lt;/p&gt;

&lt;p&gt;Built with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SvelteKit (adapter-node)&lt;/li&gt;
&lt;li&gt;Tailwind CSS&lt;/li&gt;
&lt;li&gt;Node.js&lt;/li&gt;
&lt;li&gt;Drizzle ORM&lt;/li&gt;
&lt;li&gt;Turso&lt;/li&gt;
&lt;li&gt;Cloudflare R2&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why This Architecture Matters
&lt;/h3&gt;

&lt;p&gt;This isn’t just a side project.&lt;/p&gt;

&lt;p&gt;It demonstrates:&lt;/p&gt;

&lt;p&gt;• Cloud-native storage design&lt;br&gt;
• Separation of structured vs object storage&lt;br&gt;
• Distributed edge database usage&lt;br&gt;
• S3-compatible infrastructure&lt;br&gt;
• Full-stack + DevOps ownership&lt;br&gt;
• Production-ready Linux deployment (systemd / PM2 / Nginx)&lt;/p&gt;

&lt;p&gt;Modern full-stack engineering isn’t just React + API.&lt;/p&gt;

&lt;p&gt;It’s choosing the right storage layer for the right responsibility.&lt;/p&gt;

&lt;p&gt;Database for data integrity.&lt;br&gt;
Object storage for files.&lt;br&gt;
Edge for performance.&lt;br&gt;
Node for control.&lt;/p&gt;

&lt;p&gt;Cliff Drop is small - but architected like a real system.&lt;/p&gt;

&lt;p&gt;If you’re building side projects, stop defaulting to “everything in one server.”&lt;/p&gt;

&lt;p&gt;Split storage properly.&lt;br&gt;
Think about latency.&lt;br&gt;
Think about scale.&lt;br&gt;
Think about cost.&lt;/p&gt;

&lt;p&gt;That’s how you level up.&lt;/p&gt;

&lt;p&gt;Github Repo: &lt;a href="https://github.com/RussellJapheth/cliff-drop" rel="noopener noreferrer"&gt;https://github.com/RussellJapheth/cliff-drop&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>libsql</category>
      <category>cloudcomputing</category>
      <category>svelte</category>
    </item>
    <item>
      <title>Did you know you can change how SvelteKit bundles your entire app?</title>
      <dc:creator>Russell 👨🏾‍💻</dc:creator>
      <pubDate>Fri, 13 Feb 2026 13:15:58 +0000</pubDate>
      <link>https://dev.to/_russell/did-you-know-you-can-change-how-sveltekit-bundles-your-entire-app-3ne0</link>
      <guid>https://dev.to/_russell/did-you-know-you-can-change-how-sveltekit-bundles-your-entire-app-3ne0</guid>
      <description>&lt;p&gt;When using adapter-static, most people focus on prerendering and deployment targets. But there is another lever that quietly affects performance and portability: output.bundleStrategy.&lt;/p&gt;

&lt;p&gt;By default, SvelteKit splits your app into multiple chunks for better caching and lazy loading. That is usually what you want.&lt;/p&gt;

&lt;p&gt;But if you are building:&lt;br&gt;
• A fully static site&lt;br&gt;
 • An embeddable widget&lt;br&gt;
 • A portable demo&lt;br&gt;
 • A microfrontend&lt;br&gt;
 • Something that needs to work cleanly from simple static hosting&lt;/p&gt;

&lt;p&gt;You can switch strategies.&lt;br&gt;
For example:&lt;br&gt;
 split → multiple JS/CSS files, better caching&lt;br&gt;
 single → one JS file and one CSS file&lt;br&gt;
 inline → everything injected directly into the HTML&lt;/p&gt;

&lt;p&gt;With adapter-static, this can make your build output dramatically simpler, depending on your use case.&lt;br&gt;
Sometimes performance and portability decisions start in your config file, not your components.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;adapter&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;@sveltejs/adapter-static&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="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;kit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;bundleStrategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;single&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>webdev</category>
      <category>programming</category>
      <category>svelte</category>
      <category>devops</category>
    </item>
    <item>
      <title>Before You Reach for State, Try element.dataset</title>
      <dc:creator>Russell 👨🏾‍💻</dc:creator>
      <pubDate>Wed, 11 Feb 2026 17:21:56 +0000</pubDate>
      <link>https://dev.to/_russell/before-you-reach-for-state-try-elementdataset-5443</link>
      <guid>https://dev.to/_russell/before-you-reach-for-state-try-elementdataset-5443</guid>
      <description>&lt;p&gt;There is a small JavaScript feature that quietly solves a surprising number of frontend problems, yet it rarely gets discussed:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;element.dataset&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Most developers reach for IDs, classes, or extra state before remembering that the platform already provides a structured way to attach metadata directly to DOM elements.&lt;/p&gt;

&lt;p&gt;That is where &lt;code&gt;data-*&lt;/code&gt; attributes and &lt;code&gt;dataset&lt;/code&gt; come in.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is &lt;code&gt;dataset&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;Whenever you define a custom &lt;code&gt;data-*&lt;/code&gt; attribute in HTML, JavaScript automatically exposes it through the &lt;code&gt;dataset&lt;/code&gt; property of that element.&lt;/p&gt;

&lt;p&gt;Instead of parsing attributes manually, you get a clean object interface.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; 
  &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn primary"&lt;/span&gt;
  &lt;span class="na"&gt;data-user-id=&lt;/span&gt;&lt;span class="s"&gt;"42"&lt;/span&gt;
  &lt;span class="na"&gt;data-role=&lt;/span&gt;&lt;span class="s"&gt;"admin"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  View
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "42"&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// "admin"&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the transformation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;data-user-id&lt;/code&gt; → &lt;code&gt;dataset.userId&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data-role&lt;/code&gt; → &lt;code&gt;dataset.role&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hyphenated attribute names automatically convert to camelCase.&lt;/p&gt;

&lt;p&gt;No string parsing. No extra selectors. No brittle logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters in Real Projects
&lt;/h2&gt;

&lt;p&gt;This is not just syntactic sugar. It is a practical pattern that keeps logic close to the element it belongs to.&lt;/p&gt;

&lt;p&gt;Here are a few places where &lt;code&gt;dataset&lt;/code&gt; shines:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Event Delegation
&lt;/h3&gt;

&lt;p&gt;When handling clicks on dynamic lists, you often need context about the clicked element.&lt;/p&gt;

&lt;p&gt;Instead of maintaining separate lookup maps or encoding values in IDs, you can attach structured data directly to the element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"users"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;data-user-id=&lt;/span&gt;&lt;span class="s"&gt;"101"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Alice&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;data-user-id=&lt;/span&gt;&lt;span class="s"&gt;"102"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Bob&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Selected user:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean. Scalable. Minimal state.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Lightweight UI State
&lt;/h3&gt;

&lt;p&gt;For small interactive components, not everything needs a global store or reactive system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;data-expanded=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expanded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is explicit and easy to inspect in DevTools.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Avoiding Over-Engineering
&lt;/h3&gt;

&lt;p&gt;Modern frontend ecosystems encourage abstraction. Sometimes that is necessary. Often, it is not.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;dataset&lt;/code&gt; can eliminate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extra wrapper objects&lt;/li&gt;
&lt;li&gt;Redundant DOM queries&lt;/li&gt;
&lt;li&gt;Temporary state variables&lt;/li&gt;
&lt;li&gt;Overcomplicated component logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sometimes the simplest solution is already built into the platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Few Things to Keep in Mind
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;All &lt;code&gt;dataset&lt;/code&gt; values are strings. Convert them if you need numbers or booleans.&lt;/li&gt;
&lt;li&gt;It is best suited for UI-related metadata, not large or sensitive data.&lt;/li&gt;
&lt;li&gt;Keep naming consistent and predictable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;Good frontend engineering is not just about frameworks. It is about understanding the underlying platform well enough to use it intentionally.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dataset&lt;/code&gt; is one of those small features that, when used properly, makes your code cleaner, clearer, and easier to maintain.&lt;/p&gt;

&lt;p&gt;Sometimes the most underrated tools are the ones that have been there all along.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>html</category>
      <category>javascript</category>
      <category>vanilla</category>
    </item>
    <item>
      <title>What I Learned Building a Minimalist Time Tracker with Svelte and Copilot</title>
      <dc:creator>Russell 👨🏾‍💻</dc:creator>
      <pubDate>Mon, 09 Feb 2026 14:11:01 +0000</pubDate>
      <link>https://dev.to/_russell/what-i-learned-building-a-minimalist-time-tracker-with-svelte-and-copilot-348a</link>
      <guid>https://dev.to/_russell/what-i-learned-building-a-minimalist-time-tracker-with-svelte-and-copilot-348a</guid>
      <description>&lt;p&gt;This past weekend, I shipped a small but complete time-tracking and productivity application.&lt;/p&gt;

&lt;p&gt;The goal was straightforward: build a minimalist focus timer with structured work and rest cycles, light presets, and a simple todo list, designed to reduce cognitive load and avoid feature bloat.&lt;br&gt;
From a technical standpoint, the project was built with Svelte, using modern frontend engineering best practices, clean component architecture, and a strong emphasis on performance and maintainability. I also integrated AI-assisted development into the workflow to accelerate ideation, iteration, and refactoring while keeping full control over system design and code quality.&lt;/p&gt;

&lt;p&gt;Why share this publicly?&lt;br&gt;
In the current climate, there is a lot of discussion around AI in software development. I think it is important to demonstrate practical, real-world usage where AI augments productivity without compromising engineering discipline, code ownership, or long-term maintainability.&lt;/p&gt;

&lt;p&gt;This repository is public and documents:&lt;/p&gt;

&lt;p&gt;· Frontend development with Svelte&lt;br&gt;
· AI-assisted software engineering workflows&lt;br&gt;
· UI/UX design for focus and productivity tools&lt;br&gt;
· Component-based architecture and clean state management&lt;br&gt;
· Rapid prototyping to production-ready code&lt;/p&gt;

&lt;p&gt;GitHub repository: &lt;a href="https://lnkd.in/dpqdT7Gj" rel="noopener noreferrer"&gt;https://lnkd.in/dpqdT7Gj&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Always happy to connect with engineers interested in development, product engineering, AI-augmented workflows, and building thoughtful, user-centric software.&lt;/p&gt;

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

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

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

</description>
      <category>svelte</category>
      <category>githubcopilot</category>
      <category>git</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Understanding Cron: Automating Tasks in Linux</title>
      <dc:creator>Russell 👨🏾‍💻</dc:creator>
      <pubDate>Tue, 21 Jan 2025 22:58:02 +0000</pubDate>
      <link>https://dev.to/_russell/understanding-cron-automating-tasks-in-linux-54lp</link>
      <guid>https://dev.to/_russell/understanding-cron-automating-tasks-in-linux-54lp</guid>
      <description>&lt;p&gt;Cron is a time-based job scheduling utility in Unix-like operating systems, including Linux and macOS. It allows users to automate repetitive tasks by running scripts or commands at specified intervals. Cron is widely used for system maintenance, backups, monitoring, and automating various tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Cron Works
&lt;/h2&gt;

&lt;p&gt;Cron runs as a background process and reads job schedules from a file called the &lt;strong&gt;crontab&lt;/strong&gt; (short for "cron table"). Each user on a system can have their own crontab, defining tasks to be executed at specific times.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cron Daemon
&lt;/h3&gt;

&lt;p&gt;The cron daemon (&lt;code&gt;crond&lt;/code&gt;) is responsible for executing scheduled tasks. It runs continuously in the background, checking the crontab files every minute to determine if any tasks need to be executed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cron Syntax
&lt;/h2&gt;

&lt;p&gt;Cron job entries follow a specific syntax consisting of five time fields followed by the command to execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* * * * * command-to-be-executed
| | | | |
| | | | +---- Day of the week (0 - 6) [Sunday=0]
| | | +------ Month (1 - 12)
| | +-------- Day of the month (1 - 31)
| +---------- Hour (0 - 23)
+------------ Minute (0 - 59)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Examples of Cron Jobs
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run a script every day at 2:30 AM:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   30 2 * * * /path/to/script.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run a script every Monday at 9 AM:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   0 9 * * 1 /path/to/script.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run a task every 5 minutes:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   */5 * * * * /path/to/command
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run a backup at midnight on the 1st of every month:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   0 0 1 * * /path/to/backup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Managing Cron Jobs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Viewing Cron Jobs
&lt;/h3&gt;

&lt;p&gt;To list the cron jobs for the current user, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;crontab &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Editing Cron Jobs
&lt;/h3&gt;

&lt;p&gt;To add or modify cron jobs, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;crontab &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will open the user's crontab file in the default text editor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Removing Cron Jobs
&lt;/h3&gt;

&lt;p&gt;To delete all cron jobs for the current user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;crontab &lt;span class="nt"&gt;-r&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Special Strings in Cron
&lt;/h2&gt;

&lt;p&gt;Cron also supports special keywords to simplify scheduling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@reboot&lt;/code&gt; – Run once at system startup&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@yearly&lt;/code&gt; – Equivalent to &lt;code&gt;0 0 1 1 *&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@monthly&lt;/code&gt; – Equivalent to &lt;code&gt;0 0 1 * *&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@weekly&lt;/code&gt; – Equivalent to &lt;code&gt;0 0 * * 0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@daily&lt;/code&gt; – Equivalent to &lt;code&gt;0 0 * * *&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@hourly&lt;/code&gt; – Equivalent to &lt;code&gt;0 * * * *&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;@daily /path/to/daily_task.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Logging and Troubleshooting
&lt;/h2&gt;

&lt;p&gt;Cron logs can be found in the system log files. On most Linux systems, check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /var/log/syslog | &lt;span class="nb"&gt;grep &lt;/span&gt;cron
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a cron job is not running, ensure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The script has executable permissions.&lt;/li&gt;
&lt;li&gt;The full path to the script or command is specified.&lt;/li&gt;
&lt;li&gt;Environment variables are correctly set.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Use Cases
&lt;/h2&gt;

&lt;p&gt;Cron is commonly used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automating backups&lt;/li&gt;
&lt;li&gt;Sending periodic reports&lt;/li&gt;
&lt;li&gt;Cleaning temporary files&lt;/li&gt;
&lt;li&gt;Syncing data with remote servers&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;p&gt;For an easy way to generate and understand cron expressions, visit &lt;a href="https://crontab.guru" rel="noopener noreferrer"&gt;crontab.guru&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Cron is a powerful tool for task automation in Unix-based systems. Mastering cron allows system administrators and developers to streamline operations and ensure tasks are executed efficiently without manual intervention.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>devops</category>
    </item>
    <item>
      <title>An Introduction to PHP and SQLite</title>
      <dc:creator>Russell 👨🏾‍💻</dc:creator>
      <pubDate>Mon, 20 Jan 2025 22:21:29 +0000</pubDate>
      <link>https://dev.to/_russell/an-introduction-to-php-and-sqlite-2agg</link>
      <guid>https://dev.to/_russell/an-introduction-to-php-and-sqlite-2agg</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;PHP and SQLite are a powerful combination for lightweight web applications and projects that require an embedded database solution. PHP is a popular server-side scripting language, while SQLite is a self-contained, serverless database engine. Together, they provide a simple yet effective way to build data-driven applications without the overhead of a traditional database server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding PHP
&lt;/h3&gt;

&lt;p&gt;PHP (Hypertext Preprocessor) is an open-source scripting language designed for web development. It allows developers to create dynamic and interactive web pages by embedding code within HTML. Some key features of PHP include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy integration with various databases&lt;/li&gt;
&lt;li&gt;Cross-platform compatibility&lt;/li&gt;
&lt;li&gt;Extensive community support&lt;/li&gt;
&lt;li&gt;Built-in functions for handling forms, cookies, and sessions&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Installation and Setup
&lt;/h4&gt;

&lt;p&gt;To get started with PHP:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download and install PHP from the official website.&lt;/li&gt;
&lt;li&gt;Configure the &lt;code&gt;php.ini&lt;/code&gt; file for necessary settings.&lt;/li&gt;
&lt;li&gt;Run PHP scripts using the built-in server or via a web server like Apache.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Understanding SQLite
&lt;/h3&gt;

&lt;p&gt;SQLite is a lightweight, serverless database management system stored in a single file on disk. It is widely used for small to medium-sized applications, mobile applications, and embedded systems.&lt;/p&gt;

&lt;p&gt;Key features of SQLite include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Self-contained and zero-configuration&lt;/li&gt;
&lt;li&gt;High-speed performance for small-scale applications&lt;/li&gt;
&lt;li&gt;ACID-compliant transactions&lt;/li&gt;
&lt;li&gt;Cross-platform support&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Advantages of SQLite
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;No need for a separate database server&lt;/li&gt;
&lt;li&gt;Simple setup and portability&lt;/li&gt;
&lt;li&gt;Low memory and disk space requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Setting Up PHP and SQLite
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Ensure PHP is installed on your system.&lt;/li&gt;
&lt;li&gt;Verify that SQLite is enabled by checking the PHP configuration:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;   &lt;span class="nb"&gt;phpinfo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Use the following command to confirm SQLite support:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;   &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;extension_loaded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'sqlite3'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'SQLite enabled'&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'SQLite not enabled'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Working with SQLite in PHP
&lt;/h3&gt;

&lt;p&gt;To interact with SQLite databases in PHP, the SQLite3 extension or PHP Data Objects (PDO) can be used.&lt;/p&gt;

&lt;h4&gt;
  
  
  Connecting to SQLite
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SQLite3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'database.db'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Connected successfully"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Connection failed"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Creating a Database Table
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Inserting Data
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"INSERT INTO users (name, email) VALUES ('John Doe', 'john@example.com')"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Querying Data
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SELECT * FROM users"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fetchArray&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"User: "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;" - Email: "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using PHP PDO with SQLite
&lt;/h3&gt;

&lt;p&gt;PDO offers a more flexible and secure way to interact with SQLite databases.&lt;/p&gt;

&lt;h4&gt;
  
  
  Establishing a Connection
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$pdo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PDO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'sqlite:database.db'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$pdo&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PDO&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ATTR_ERRMODE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;PDO&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ERRMODE_EXCEPTION&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Connected successfully"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PDOException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Connection failed: "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Executing Queries
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$pdo&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"INSERT INTO users (name, email) VALUES (:name, :email)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Jane Doe'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'jane@example.com'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Building a Simple CRUD Application
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Setup the Project Structure:&lt;/strong&gt; Organize files into &lt;code&gt;index.php&lt;/code&gt;, &lt;code&gt;db.php&lt;/code&gt;, and &lt;code&gt;functions.php&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a Database Schema:&lt;/strong&gt; Define tables and relationships.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement CRUD Operations:&lt;/strong&gt; Use PHP to insert, retrieve, update, and delete records.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Display Data:&lt;/strong&gt; Render results in HTML tables.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Best Practices for PHP and SQLite
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security:&lt;/strong&gt; Use prepared statements to prevent SQL injection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; Optimize queries and indexes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backup:&lt;/strong&gt; Regularly backup the SQLite database file.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Common Issues and Troubleshooting
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ensure correct file permissions for the database.&lt;/li&gt;
&lt;li&gt;Handle concurrency issues with proper locking mechanisms.&lt;/li&gt;
&lt;li&gt;Use error handling to debug connection failures.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;PHP and SQLite provide an excellent foundation for lightweight web applications and embedded solutions. With their ease of use and flexibility, they are a great choice for developers looking to build scalable yet simple applications.&lt;/p&gt;

&lt;h4&gt;
  
  
  Further Reading
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;PHP Official Documentation: &lt;a href="https://www.php.net" rel="noopener noreferrer"&gt;https://www.php.net&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SQLite Documentation: &lt;a href="https://www.sqlite.org" rel="noopener noreferrer"&gt;https://www.sqlite.org&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>php</category>
      <category>sqlite</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Systemd vs. Docker: Exploring a Surprising Alternative</title>
      <dc:creator>Russell 👨🏾‍💻</dc:creator>
      <pubDate>Mon, 09 Dec 2024 22:04:04 +0000</pubDate>
      <link>https://dev.to/_russell/systemd-vs-docker-exploring-a-surprising-alternative-4pmm</link>
      <guid>https://dev.to/_russell/systemd-vs-docker-exploring-a-surprising-alternative-4pmm</guid>
      <description>&lt;p&gt;&lt;strong&gt;What is Docker?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Docker is a platform that allows you to build, run, and manage containers. Containers are like lightweight virtual machines, but they don't emulate the entire operating system. Instead, they provide a sandboxed environment that mimics a virtual machine, allowing you to isolate and run applications with their dependencies in a consistent environment, regardless of the underlying host system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Systemd?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Systemd is an init system and a process manager for Linux. It’s responsible for bootstrapping the user space and managing system processes, including services that run in the background. Systemd can be used to start, stop, and manage processes and services, and it integrates deeply with the operating system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why This Article?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At first glance, Docker and Systemd may seem like two very different tools, but both are used to manage the execution of applications. Docker is often used to run applications in isolated containers, while Systemd is traditionally used to manage services that run directly on the host operating system. In this article, we’ll compare these two approaches by setting up Pocketbase, a lightweight backend server, in both environments: one using Docker and the other using Systemd to run it as a regular process.&lt;/p&gt;

&lt;p&gt;We'll explore the differences in setup, management, and performance of both approaches and discuss when you might prefer one over the other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before we begin, make sure you have the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A Linux-based system (Ubuntu, CentOS, Debian, etc.).&lt;/li&gt;
&lt;li&gt;Docker installed, if you want to run the application in a container.&lt;/li&gt;
&lt;li&gt;Basic knowledge of the command line.&lt;/li&gt;
&lt;li&gt;The Pocketbase binary. Find the right one for your machine &lt;a href="https://pocketbase.io/docs/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Running Pocketbase in Docker
&lt;/h3&gt;

&lt;p&gt;First, let's look at how to run Pocketbase using Docker. Docker makes it simple to get Pocketbase up and running quickly by encapsulating the application and all its dependencies within a container. Here’s how you would run Pocketbase in Docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8090:8090 &lt;span class="nt"&gt;-v&lt;/span&gt; ~/pocketbase_data:/pocketbase pocketbase/pocketbase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-d&lt;/code&gt;: Runs the container in detached mode.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-p 8090:8090&lt;/code&gt;: Maps port 8090 on the host to port 8090 in the container.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-v ~/pocketbase_data:/pocketbase&lt;/code&gt;: Mounts the local directory &lt;code&gt;~/pocketbase_data&lt;/code&gt; to &lt;code&gt;/pocketbase&lt;/code&gt; inside the container to persist data.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pocketbase/pocketbase&lt;/code&gt;: Specifies the Pocketbase image to run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Docker, you get a clean, isolated environment, and managing the application is as simple as running or stopping the container. Docker also ensures that your Pocketbase instance runs in a consistent environment, regardless of what system it’s on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running Pocketbase with Systemd
&lt;/h3&gt;

&lt;p&gt;Now, let’s look at how we can run Pocketbase as a regular process using Systemd. With Systemd, you will be directly managing the Pocketbase process, and it will be tied to the underlying system, rather than running in a container. Here’s how to do it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a Systemd Service File&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/systemd/system/pocketbase.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add the following content to the file&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Pocketbase Application&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network.target&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/local/bin/pocketbase serve --http=0.0.0.0:8090&lt;/span&gt;
&lt;span class="py"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/yourusername/pocketbase&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yourusername&lt;/span&gt;
&lt;span class="py"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yourgroup&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reload Systemd and Start the Service&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start pocketbase
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;pocketbase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ExecStart&lt;/code&gt;: Specifies the command to run Pocketbase.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WorkingDirectory&lt;/code&gt;: Sets the directory where Pocketbase will run (you can adjust this to your Pocketbase installation path).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Restart=always&lt;/code&gt;: Ensures that the process is restarted if it crashes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;User&lt;/code&gt; and &lt;code&gt;Group&lt;/code&gt;: Specifies the user and group that should run the process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Systemd, you manage the Pocketbase process like any other system service. You can control it using standard Systemd commands like &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;stop&lt;/code&gt;, &lt;code&gt;restart&lt;/code&gt;, and &lt;code&gt;status&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparison: Docker vs. Systemd
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ease of Setup&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker offers a more streamlined setup with fewer dependencies since it isolates the application within a container. It’s a great choice if you need a quick, portable solution that works across different environments.&lt;/li&gt;
&lt;li&gt;Systemd requires setting up a service file, managing dependencies, and ensuring the application runs directly on the system. It's a more manual process but offers deeper integration with the host OS.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Isolation&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker provides full isolation for the application, meaning it runs in a self-contained environment. This is especially useful when you want to avoid conflicts with other applications or dependencies on your system.&lt;/li&gt;
&lt;li&gt;Systemd runs the application directly on the system, which can lead to potential conflicts with other system services if not properly managed, but it allows for better integration with the system’s process management.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Management and Monitoring&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker simplifies management by encapsulating all the application dependencies within the container. You can easily stop, restart, or scale the application by interacting with the Docker container.&lt;/li&gt;
&lt;li&gt;Systemd gives you more control over how the process interacts with the system. You can manage logging, resource allocation, and service dependencies more granularly with Systemd.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker adds a small overhead due to containerization, though the performance difference is usually negligible for most applications.&lt;/li&gt;
&lt;li&gt;Systemd typically offers better performance for long-running processes since there’s no container overhead, and it runs natively on the host system.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

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

&lt;p&gt;In this article, we’ve explored two different methods for running Pocketbase: inside a Docker container and as a regular process managed by Systemd. Docker is ideal for creating isolated environments and easily managing dependencies, while Systemd gives you more control and deeper integration with the host system.&lt;/p&gt;

&lt;p&gt;Which method you choose depends on your needs. If portability and isolation are key, Docker might be the better choice. If you prefer direct integration with the operating system and need more control over service management, Systemd is a great option. Both approaches have their strengths, and understanding them can help you make the right decision for your application deployment.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/dyYINktERj4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>devops</category>
      <category>webdev</category>
      <category>docker</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building CLI Tools with Go (Golang): A JSON File Formatter</title>
      <dc:creator>Russell 👨🏾‍💻</dc:creator>
      <pubDate>Wed, 20 Mar 2024 10:36:14 +0000</pubDate>
      <link>https://dev.to/_russell/building-cli-tools-with-go-golang-a-json-file-formatter-2855</link>
      <guid>https://dev.to/_russell/building-cli-tools-with-go-golang-a-json-file-formatter-2855</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Go is a simple language, it supports concurrency out of the box and compiles to executable files so users do not need to have Go installed to run our apps, this makes Go ideally suited for building CLI tools. In this article, we will be going over how to build a CLI utility to format JSON files in Go.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;A working Go installation. You can find instructions &lt;a href="https://go.dev/doc/install"&gt;here&lt;/a&gt; to set up Go if you do not.&lt;/li&gt;
&lt;li&gt;A code editor (VsCode is a popular choice).&lt;/li&gt;
&lt;li&gt;This article assumes you have basic knowledge of programming concepts and the CLI.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting ready
&lt;/h2&gt;

&lt;p&gt;To confirm you have Go installed and ready to use run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get a response like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go version go1.22.1 linux/amd64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you did not, please follow the instructions &lt;a href="https://go.dev/doc/install"&gt;here&lt;/a&gt; to install Go. &lt;/p&gt;

&lt;p&gt;With that out of the way here is the flow of our tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// 1. Get the path for our input file from &lt;/span&gt;
&lt;span class="c"&gt;//    the command line arguments&lt;/span&gt;
&lt;span class="c"&gt;// 2. Check if the input file is readable and contains &lt;/span&gt;
&lt;span class="c"&gt;//    valid JSON&lt;/span&gt;
&lt;span class="c"&gt;// 3. Format the contents of the file&lt;/span&gt;
&lt;span class="c"&gt;// 4. Save formatted JSON to another file&lt;/span&gt;
&lt;span class="c"&gt;// 5. Compile our binary&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 0:
&lt;/h3&gt;

&lt;p&gt;In a folder of your choosing, create a main.go file using your code editor. Here are the contents of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// 1. Get the path for our input file from &lt;/span&gt;
&lt;span class="c"&gt;//    the command line arguments&lt;/span&gt;
&lt;span class="c"&gt;// 2. Check if the input file is readable and contains &lt;/span&gt;
&lt;span class="c"&gt;//    valid JSON&lt;/span&gt;
&lt;span class="c"&gt;// 3. Format the contents of the file&lt;/span&gt;
&lt;span class="c"&gt;// 4. Save formatted JSON to another file&lt;/span&gt;
&lt;span class="c"&gt;// 5. Compile our binary&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"fmt"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, 你好"&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;To execute the following code in your CLI run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go run main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which would give us the output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello, 你好
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1:
&lt;/h3&gt;

&lt;p&gt;First, we check that we have at least 1 argument passed to our program; the input file. To do this we would need to import the OS module&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We check that at least 1 argument was passed and display an error otherwise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Get the arguments passed to our program&lt;/span&gt;
&lt;span class="n"&gt;arguments&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;// Check that at least 1 argument was passed&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Display error message and exit the program&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Missing required arguments"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Usage: go run main.go input_file.json"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2
&lt;/h3&gt;

&lt;p&gt;Next, we confirm that our input file is readable and contains valid JSON&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// We add the encoding/json, errors and bytes module&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"bytes"&lt;/span&gt;
    &lt;span class="s"&gt;"encoding/json"&lt;/span&gt;
    &lt;span class="s"&gt;"errors"&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// A function to check if a string is valid JSON&lt;/span&gt;
&lt;span class="c"&gt;// src: https://stackoverflow.com/a/22129435&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;isJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;js&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// We define a function to check if the file exists&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;FileExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrNotExist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// A function to pretty print JSON strings&lt;/span&gt;
&lt;span class="c"&gt;// src: https://stackoverflow.com/a/36544455 (modified)&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;jsonPrettyPrint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Buffer&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Indent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"    "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c"&gt;// In our main function, we check if the file exists&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;....&lt;/span&gt;
    &lt;span class="c"&gt;// Call FileExists function with the file path&lt;/span&gt;
    &lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;FileExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="c"&gt;// Check the result&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sorry the file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s"&gt;"does not exist!"&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c"&gt;// just pass the file name&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// convert the files contents to string&lt;/span&gt;
    &lt;span class="n"&gt;contents&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// check if the string is valid json&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;contents&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;isJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid or empty JSON file"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3
&lt;/h3&gt;

&lt;p&gt;We format the contents of the file and display it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;    &lt;span class="c"&gt;// print the formatted string; this output can be piped to an output file&lt;/span&gt;
    &lt;span class="c"&gt;// no prefix and indenting with 4 spaces&lt;/span&gt;
    &lt;span class="n"&gt;formatted&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;jsonPrettyPrint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Display the formatted string&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;formatted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4
&lt;/h3&gt;

&lt;p&gt;Save formatted JSON to another file, and to do that we pipe the output to a file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go run main.go input.json &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; out.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The angle brackets &lt;code&gt;&amp;gt;&lt;/code&gt;  redirects the output of our program to a file &lt;code&gt;out.json&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5
&lt;/h3&gt;

&lt;p&gt;Compile our code to a single binary. To do this we run&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;# Build our code&lt;/span&gt;
go build main.go

&lt;span class="c"&gt;# list the contents of the current directory&lt;/span&gt;
&lt;span class="c"&gt;# we would have an executable binary called "main"&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="c"&gt;# main main.go&lt;/span&gt;

&lt;span class="c"&gt;#We will rename our executable prettyJson&lt;/span&gt;
&lt;span class="nb"&gt;mv &lt;/span&gt;main prettyJson
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can run our new binary as we would any other executable&lt;/p&gt;

&lt;p&gt;Let's update our usage instructions&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// fmt.Println("Usage: go run main.go input_file.json")&lt;/span&gt;
&lt;span class="c"&gt;// becomes&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Usage: ./prettyJson input_file.json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the completed code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// 1. Get the path for our input file from&lt;/span&gt;
&lt;span class="c"&gt;//    the command line arguments&lt;/span&gt;
&lt;span class="c"&gt;// 2. Check if the input file is readable and contains&lt;/span&gt;
&lt;span class="c"&gt;//    valid JSON&lt;/span&gt;
&lt;span class="c"&gt;// 3. Format the contents of the file&lt;/span&gt;
&lt;span class="c"&gt;// 4. Save formatted JSON to another file&lt;/span&gt;
&lt;span class="c"&gt;// 5. Compile our binary&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"bytes"&lt;/span&gt;
    &lt;span class="s"&gt;"encoding/json"&lt;/span&gt;
    &lt;span class="s"&gt;"errors"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// A function to check if a string is valid JSON&lt;/span&gt;
&lt;span class="c"&gt;// src: https://stackoverflow.com/a/22129435 (modified)&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;isJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;js&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;FileExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrNotExist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// A function to pretty print JSON strings&lt;/span&gt;
&lt;span class="c"&gt;// src: https://stackoverflow.com/a/36544455&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;jsonPrettyPrint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Buffer&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Indent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Get the arguments passed to our program&lt;/span&gt;
    &lt;span class="n"&gt;arguments&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c"&gt;// Check that at least 1 argument was passed&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Display error message and exit the program&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Missing required argument"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Usage: ./prettyJson input_file.json"&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="c"&gt;// Call FileExists function with the file path&lt;/span&gt;
    &lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;FileExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="c"&gt;// Check the result&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sorry the file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s"&gt;"does not exist!"&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c"&gt;// just pass the file name&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// convert the files contents to string&lt;/span&gt;
    &lt;span class="n"&gt;contents&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// check if the string is valid json&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;contents&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;isJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid or empty JSON file"&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="c"&gt;// print the formatted string; this output can be piped to an output file&lt;/span&gt;
    &lt;span class="c"&gt;// no prefix and indenting with 4 spaces&lt;/span&gt;
    &lt;span class="n"&gt;formatted&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;jsonPrettyPrint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Display the formatted string&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;formatted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



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

&lt;p&gt;We have seen how to create a JSON formatter utility with Go. The code is available as a GitHub gist &lt;a href="https://gist.github.com/RussellJapheth/5c317783490afb83583bcc67c7572cac"&gt;here&lt;/a&gt;. Feel free to make improvements.&lt;/p&gt;

</description>
      <category>go</category>
      <category>cli</category>
      <category>devops</category>
      <category>json</category>
    </item>
    <item>
      <title>Configuring Wildcard Subdomains: A Comparison of Nginx and Caddy</title>
      <dc:creator>Russell 👨🏾‍💻</dc:creator>
      <pubDate>Fri, 23 Feb 2024 15:25:39 +0000</pubDate>
      <link>https://dev.to/_russell/configuring-wildcard-subdomains-a-comparison-of-nginx-and-caddy-d6e</link>
      <guid>https://dev.to/_russell/configuring-wildcard-subdomains-a-comparison-of-nginx-and-caddy-d6e</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Wildcard subdomains allow you to dynamically handle multiple subdomains under a parent domain without explicitly defining the configuration for each subdomain. &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%2Fvoy3xj8mp4rs7f1o6pqi.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%2Fvoy3xj8mp4rs7f1o6pqi.png" alt="An illustration of a multitenant application with subdomains"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A common use case for wildcard subdomains is in multitenant applications where each customer accesses the application from their unique domain. Manually configuring each subdomain is impractical in this scenario.&lt;/p&gt;

&lt;p&gt;In this article, we will look at configuring wildcard subdomains on Nginx and Caddy and compare both options.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The article assumes you have Nginx and Caddy installed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Nginx Configuration
&lt;/h3&gt;

&lt;p&gt;To get started, we'll create a new config file. You need root access to update the configuration so we will prefix the command with &lt;code&gt;sudo&lt;/code&gt;&lt;/p&gt;

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

&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/nginx/sites-available/app.com


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

&lt;/div&gt;

&lt;p&gt;Add the following configuration to the file&lt;/p&gt;

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

server {

    server_name *.app.com;

    location / {
        proxy_pass http://127.0.0.1:8090;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}



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

&lt;/div&gt;

&lt;p&gt;This configuration proxies all requests to a service running on port 8090. The key bit is &lt;code&gt;server_name *.app.com;&lt;/code&gt;, this tells nginx to handle all requests made to &lt;code&gt;*.app.com&lt;/code&gt; (customer1.app.com, customer2.app.com ...).&lt;/p&gt;

&lt;h3&gt;
  
  
  Caddy Configuration
&lt;/h3&gt;

&lt;p&gt;Just like Nginx, we'll begin by editing the main configuration file and prefixing the call with &lt;code&gt;sudo&lt;/code&gt; since it requires root access:&lt;/p&gt;

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

&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/caddy/Caddyfile


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

&lt;/div&gt;

&lt;p&gt;And the contents: &lt;/p&gt;


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

&lt;p&gt;*.app.com {&lt;br&gt;
    request_body {&lt;br&gt;
        max_size 10MB&lt;br&gt;
    }&lt;br&gt;
    reverse_proxy 127.0.0.1:8090 {&lt;br&gt;
        transport http {&lt;br&gt;
            read_timeout 360s&lt;br&gt;
        }&lt;br&gt;
    }&lt;br&gt;
}&lt;/p&gt;

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

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Comparison and Conclusion&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;Both Nginx and Caddy have similar configuration patterns they both have distinct approaches so the syntax differs greatly. They however both provide nearly the same levels of flexibility and functionality with Caddy being more akin to JSON and supporting it natively.&lt;/p&gt;

</description>
      <category>nginx</category>
      <category>devops</category>
      <category>webdev</category>
      <category>caddy</category>
    </item>
    <item>
      <title>Deploying Pocketbase with Docker, Nginx and SSL</title>
      <dc:creator>Russell 👨🏾‍💻</dc:creator>
      <pubDate>Thu, 15 Feb 2024 14:28:25 +0000</pubDate>
      <link>https://dev.to/_russell/deploying-pocketbase-with-docker-nginx-and-ssl-323l</link>
      <guid>https://dev.to/_russell/deploying-pocketbase-with-docker-nginx-and-ssl-323l</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;What is &lt;strong&gt;Pocketbase&lt;/strong&gt;? &lt;a href="https://pocketbase.io/"&gt;Pocketbase&lt;/a&gt; is an open-source backend solution offering a real-time database, file storage, and seamless user authentication with OAuth integration, all readily available right out of the box.&lt;/p&gt;

&lt;p&gt;In this post, we’ll cover how to deploy Pocketbase using Docker and Nginx on an Ubuntu machine (although the instructions would also work for any Debian-based Linux distribution). Additionally, we will cover provisioning an SSL certificate using Certbot for enhanced security.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Prerequisites&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Before we begin, ensure that you have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An Ubuntu machine or server (these instructions apply to other Debian-based Linux distributions as well).&lt;/li&gt;
&lt;li&gt;Docker installed on your system.&lt;/li&gt;
&lt;li&gt;Basic knowledge of Docker and command-line usage.&lt;/li&gt;
&lt;li&gt;Nginx installed on your system with certbot&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are articles on installing &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-20-04"&gt;docker&lt;/a&gt;, &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-20-04"&gt;nginx&lt;/a&gt; and &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-20-04"&gt;certbot&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing PocketBase With Docker
&lt;/h3&gt;

&lt;p&gt;We'll use a slightly modified version of the docker-compose.yml from the &lt;a href="https://github.com/muchobien/pocketbase-docker"&gt;muchobien/pocketbase-docker&lt;/a&gt; repository. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;. Create a docker-compose.yml file in a directory of your choice. The contents of the file are provided below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.7"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pocketbase&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/muchobien/pocketbase:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pocketbase&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--encryptionEnv&lt;/span&gt; 
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ENCRYPTION&lt;/span&gt; 
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ENCRYPTION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;YOUR_ENCRYPTION_KEY&lt;/span&gt; 
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8091:8090"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./pb_data:/pb_data&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./pb_public:/pb_public&lt;/span&gt; 
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./pb_migrations:/pb_migrations&lt;/span&gt; 
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wget --no-verbose --tries=1 --spider http://localhost:8090/api/health || exit &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;. To start the docker container we run the following command in the terminal:&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="nv"&gt;$ &lt;/span&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This starts a pocketbase instance and exposes it on port 8091. The &lt;code&gt;pb_data&lt;/code&gt;, &lt;code&gt;pb_public&lt;/code&gt;, and &lt;code&gt;pb_migrations&lt;/code&gt; folders created are bound to the folder in the container.&lt;/p&gt;

&lt;p&gt;Step 3. Next, we create an Nginx instance to give access to our application. To keep things simple, we will work with the default configuration file. Run the command below:&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="nv"&gt;$ &lt;/span&gt;nano /etc/nginx/sites-available/default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following code to the end of the file and save, replacing &lt;code&gt;app.example.com&lt;/code&gt; with your domain name or the subdomain you want to create:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    server_name app.example.com;

    location / {
        proxy_pass http://127.0.0.1:8091;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

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

&lt;/div&gt;



&lt;p&gt;Next, test that your Nginx configuration is valid:&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="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get a message like the one below if everything is fine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then restart Nginx:&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="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we generate our SSL certificates using certbot:&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="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot &lt;span class="nt"&gt;--nginx&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; app.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congrats, you have now set up PocketBase secured with SSL. The following are the routes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://app.example.com"&gt;https://app.example.com&lt;/a&gt; - Serves the static content (html, css, images, etc.) from the &lt;code&gt;pb_public&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://app.example.com/_/"&gt;https://app.example.com/_/&lt;/a&gt; - The admin dashboard UI&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://app.example.com/api/"&gt;https://app.example.com/api/&lt;/a&gt; - The REST API&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Following this tutorial, you now have deployed PocketBase successfully.&lt;/p&gt;

</description>
      <category>pocketbase</category>
      <category>docker</category>
      <category>devops</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
