<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title><![CDATA[ DevDojo ]]></title>
        <link><![CDATA[ https://devdojo.com/feed/bobbyiliev ]]></link>
        <description><![CDATA[ Learn web development and design with our on-demand video platform. Learn development through our developer courses and developer videos ]]></description>
        <language>en</language>
        <pubDate>2026-05-09 04:07:38</pubDate>

                    <item>
                <title><![CDATA[Getting Started with Docker Offload]]></title>
                <link>https://devdojo.com/bobbyiliev/getting-started-with-docker-offload</link>
                <description><![CDATA[<p>As a Docker Captain, I've tested plenty of features, but this one stands out. Docker Offload makes it possible to run builds and containers in the cloud without leaving your usual workflow.</p>
<p><a href="https://docs.docker.com/offload/">Docker Offload</a> was just announced at World Congress 2025 and it brings cloud execution to your local development flow</p>
<p>Whether you're building AI models, running compute-heavy workloads, or just tired of your fans going full throttle, this is for you.</p>
<hr>
<h3 id="why-docker-offload">💡 Why Docker Offload?</h3>
<p>If you're working on large projects with limited local resources, you've probably felt the pain:</p>
<ul>
<li>Slow build times</li>
<li>Inability to run GPU workloads locally</li>
<li>Inconsistent dev environments across the team</li>
</ul>
<p>...and nobody wants that.</p>
<p>Docker Offload solves all that. You get access to high-performance cloud infrastructure with the same Docker CLI and Docker Desktop experience you're used to.</p>
<hr>
<h3 id="key-features">🚀 Key Features</h3>
<ul>
<li><strong>Cloud builds and runs</strong> with no changes to your setup</li>
<li><strong>GPU support</strong> out of the box (NVIDIA L4)</li>
<li><strong>One command to start</strong>: <code>docker offload start</code></li>
<li><strong>Free trial minutes included</strong></li>
<li><strong>Works with Docker Compose and Model Runner</strong></li>
</ul>
<hr>
<h3 id="getting-started">🧪 Getting Started</h3>
<p>You'll need Docker Desktop 4.43 or later. You can go over the <a href="https://docs.docker.com/offload/quickstart/">Docker Offload quickstart</a> as well, but in a nutshell, it is just a matter of:</p>
<ol>
<li>Sign into Docker Desktop</li>
<li>Start Offload:</li>
</ol>
<pre><code class="hljs">docker offload start
</code></pre>
<ol start="3">
<li>Choose your account and enable GPU if needed</li>
<li>Run a container to test:</li>
</ol>
<pre><code class="hljs">docker run --rm hello-world
</code></pre>
<p>Or for GPU:</p>
<pre><code class="hljs">docker run --rm --gpus all hello-world
</code></pre>
<p>To stop the session:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<pre><code class="hljs">docker offload stop
</code></pre>
<hr>
<h3 id="why-this-is-a-big-deal">🧠 Why This Is a Big Deal</h3>
<p>If you're on an underpowered laptop: now you can run LLMs, big builds, and heavy services without blowing up your laptop.</p>
<p>If your team has mixed hardware: with Docker Offload everyone gets a consistent, fast environment.</p>
<p>If you're building agentic apps or running CI jobs: this makes local + cloud hybrid dev actually work.</p>
<hr>
<h3 id="what-you-can-do-with-docker-offload">💡 What You Can Do With Docker Offload</h3>
<p>Docker Offload brings the power of the cloud into your local Docker workflow. Here are a few things you can do right away:</p>
<ul>
<li>
<p>Build containers faster by offloading docker build to remote cloud machines with smart caching</p>
</li>
<li>
<p>Run GPU-heavy apps like machine learning pipelines, video processing, or LLM inference using NVIDIA L4 GPUs</p>
</li>
<li>
<p>Use Docker Compose to spin up full-stack apps in the cloud just like you do locally</p>
</li>
<li>
<p>Develop on low-powered machines or virtual desktops without worrying about performance limits</p>
</li>
<li>
<p>Run AI demos like Jupyter Lab, Hugging Face Transformers, or multi-agent systems without setting up any infra</p>
</li>
<li>
<p>Share the same cloud environment across your team, no matter what hardware they use</p>
</li>
<li>
<p>No special setup. No infra to manage. Just faster, smoother development with tools you already know.</p>
</li>
</ul>
<hr>
<h3 id="final-thoughts">🧵 Final Thoughts</h3>
<p>Docker Offload makes serious development easier for everyone, whether you're solo or working in a team. You keep your local flow, but your containers run on machines that don't melt under pressure.</p>
<p>You can also check out the pricing details <a href="https://docs.docker.com/offload/usage/">here</a>.</p>
<p>Try it and let me know what you think in the comments!</p>
]]></description>
                                                            <category>docker</category>
                                            <category>devops</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/13370</guid>
                <pubDate>Thu, 10 Jul 2025 03:39:21 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to Write Unit Tests for Bash Scripts (The Simple Way)]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-write-unit-tests-for-bash-scripts-the-simple-way</link>
                <description><![CDATA[<h3 id="introduction">Introduction</h3>
<p>If you enjoy writing Bash scripts to automate tasks, then you might ask yourself: "Can I write tests for my Bash scripts?".</p>
<p>The answer is: <strong>Yes, you can!</strong> And it's not hard at all.</p>
<hr>
<h3 id="what-you-need">🛠️ What You Need</h3>
<p>We will use a tool called <a href="https://github.com/bats-core/bats-core"><code>bats</code></a>. It lets you write tests for Bash scripts in a clean and simple way.</p>
<p>To install it:</p>
<pre><code class="hljspowershell">brew install bats<span class="hljs-literal">-core</span>  <span class="hljs-comment"># On macOS</span>
<span class="hljs-comment"># OR</span>
sudo apt install bats  <span class="hljs-comment"># On Ubuntu/Debian</span>
</code></pre>
<hr>
<h3 id="a-simple-bash-script-example">🧪 A Simple Bash Script Example</h3>
<p>Let’s say you have this function in <code>script.sh</code>:</p>
<pre><code class="hljspowershell"><span class="hljs-comment">#!/bin/bash</span>

say_hello() {
  echo <span class="hljs-string">"Hello, <span class="hljs-variable">$1</span>!"</span>
}
</code></pre>
<hr>
<h3 id="how-to-test-it">✅ How to Test It</h3>
<p>Create a file called <code>test_script.bats</code>:</p>
<pre><code class="hljsperl"><span class="hljs-comment">#!/usr/bin/env bats</span>

load <span class="hljs-string">'./script.sh'</span>

@test <span class="hljs-string">"say_hello prints the correct message"</span> {
  run say_hello <span class="hljs-string">"Bobby"</span>
  [ <span class="hljs-string">"$status"</span> -eq <span class="hljs-number">0</span> ]
  [ <span class="hljs-string">"$output"</span> = <span class="hljs-string">"Hello, Bobby!"</span> ]
}
</code></pre>
<p>Then run the tests with:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">bats</span> <span class="hljs-selector-tag">test_script</span><span class="hljs-selector-class">.bats</span>
</code></pre>
<hr>
<h3 id="run-tests-with-github-actions">🤖 Run Tests with GitHub Actions</h3>
<p>You can also run the tests automatically using GitHub Actions.</p>
<p>Create a <code>.github/workflows/test.yml</code> file:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<pre><code class="hljsmarkdown">name: Bash Script Tests

on:
  pull_request:
<span class="hljs-code">    branches: [ main ]</span>

jobs:
  test:
<span class="hljs-code">    runs-on: ubuntu-latest</span>
<span class="hljs-code">    steps:</span>
<span class="hljs-bullet">      - </span>uses: actions/checkout@v3
<span class="hljs-bullet">      - </span>name: Install BATS
<span class="hljs-code">        run: sudo apt-get update &amp;&amp; sudo apt-get install -y bats</span>
<span class="hljs-bullet">      - </span>name: Run tests
<span class="hljs-code">        run: bats test_script.bats</span>
</code></pre>
<p>Every time you push changes or open a pull request, your tests will run automatically. This helps you catch problems early.</p>
<hr>
<h3 id="learn-more-about-bash">📘 Learn More About Bash</h3>
<p>If you are just starting with Bash scripting, I wrote a free and open-source book that explains the basics step by step:</p>
<blockquote>
<p><a href="https://github.com/bobbyiliev/introduction-to-bash-scripting">Introduction to Bash Scripting</a></p>
</blockquote>
<p>It covers loops, variables, functions, and more. Perfect for beginners.</p>
<hr>
<p>If you want to try this on a cloud server, I use and recommend <a href="https://m.do.co/c/2a9bba940f39">DigitalOcean</a>. You'll get <strong>$200 in free credits</strong> for 60 days. Great for running your projects, testing scripts, or learning Linux.</p>
<p>If you have questions or want to chat more about Bash or DevOps, feel free to reach out on Twitter/X: <a href="https://x.com/bobbyiliev_">@bobbyiliev_</a></p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>Thanks for reading and happy scripting!</p>
<p>– Bobby</p>
]]></description>
                                                            <category>bash</category>
                                            <category>testing</category>
                                            <category>linux</category>
                                            <category>devops</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12954</guid>
                <pubDate>Fri, 11 Apr 2025 22:21:48 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[7 Best SQL Books for Beginners]]></title>
                <link>https://devdojo.com/bobbyiliev/7-best-sql-books-for-beginners</link>
                <description><![CDATA[<p>Learning SQL doesn't have to be boring or overwhelming. With the right book, it can actually be fun and super rewarding.</p>
<p>Whether you're trying to level up your backend skills, work with data more efficiently, or build your first app with a real database, t'ese beginner-friendlSQL books will help you get started. I've included both free and paid options, so there's something for every budget.</p>
<hr>
<h3 id="toc-1-introduction-to-sql-free">1. Introduction to SQL (Free)</h3>
<p>This free ebook is perfect for anyone starting their SQL journey. It covers the basics of <code>SELECT</code> statements, filtering data, using joins, and more. If you're brand new to SQL and want to get started today, this is a great resource.</p>
<p>👉 <a href="https://github.com/bobbyiliev/introduction-to-sql">Download the ebook here</a></p>
<hr>
<h3 id="toc-2-sql-for-data-analysis-by-cathy-tanimura-paid">2. SQL for Data Analysis by Cathy Tanimura (Paid)</h3>
<p>If you're into data, this book is for you. It teaches SQL in the context of real-world data analysis tasks. It's beginner-friendly, with practical examples that make it easy to follow along.</p>
<p>👉 <a href="https://amzn.to/4iO0uFO">Get the book on Amazon</a></p>
<hr>
<h3 id="toc-3-head-first-sql-by-lynn-beighley-paid">3. Head First SQL by Lynn Beighley (Paid)</h3>
<p>This is a super engaging and visual book that breaks down SQL concepts in a fun, approachable way. Perfect for beginners who want to avoid dry technical writing and learn by doing.</p>
<p>👉 <a href="https://amzn.to/4iNiEqY">Get the book on Amazon</a></p>
<hr>
<h3 id="toc-4-learning-sql-by-alan-beaulieu-paid">4. Learning SQL by Alan Beaulieu (Paid)</h3>
<p>A classic. It starts with the basics and takes you through everything from creating tables to writing complex queries. Clear explanations and practical exercises make it a solid resource for SQL newcomers.</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<p>👉 <a href="https://amzn.to/4l5Ml8e">Get the book on Amazon</a></p>
<hr>
<h3 id="toc-5-sqlbolt-free">5. SQLBolt (Free)</h3>
<p>Not a book, but a fantastic interactive tutorial that walks you through SQL concepts step by step. You can run queries right in the browser, g'eat for hands-olearners.</p>
<p>👉 <a href="https://sqlbolt.com/">Try SQLBolt</a></p>
<hr>
<h3 id="toc-6-the-art-of-sql-by-stephane-faroult-paid">6. The Art of SQL by Stéphane Faroult (Paid)</h3>
<p>Once you've got the basics down, this book will help you think like a SQL pro. It's less about syntax and more about strategy, h'w to write clean, efficienqueries.</p>
<p>👉 <a href="https://amzn.to/4258oDl">Get the book on Amazon</a></p>
<hr>
<h3 id="toc-7-sql-official-documentation-free">7. SQL Official Documentation (Free)</h3>
<p>Not exactly a book, but if you ever get stuck, the docs are your best friend. Whether you're using PostgreSQL, MySQL, or SQLite, their official docs are gold mines of information.</p>
<p>👉 <a href="https://www.postgresql.org/docs/">PostgreSQL Docs</a>
👉 <a href="https://dev.mysql.com/doc/">MySQL Docs</a>
👉 <a href="https://www.sqlite.org/docs.html">SQLite Docs</a></p>
<h3 id="toc-8-sql-courses-by-aaron-francis-paid">8. SQL Courses by Aaron Francis (Paid)</h3>
<p>If you're ready to take your SQL skills to the next level, Aaron Francis has two phenomenal courses that go beyond the basics:</p>
<ul>
<li>🚀 <a href="https://highperformancesqlite.com/">High Performance SQLite</a>: Learn how to get the most out of SQLite, especially in production.</li>
<li>🐘 <a href="https://masteringpostgres.com/">Mastering PostgreSQL</a>: A deep dive into advanced PostgreSQL features like indexing, performance tuning, and query optimization.</li>
</ul>
<p>Aaron's teaching style is clear, practical, and full of insights you can apply immediately. Follow him on <a href="https://x.com/aarondfrancis">X</a> for even more great tips.</p>
<hr>
<h3 id="conclusion">Conclusion</h3>
<p>SQL is one of the most valuable skills you can learn as a developer or data enthusiast. With these beginner-friendly books and resources, you'll be writing queries and analyzing data in no time.</p>
<p>And if you want to practice your SQL skills on a real database, you can spin up a server with my <a href="https://m.do.co/c/2a9bba940f39">DigitalOcean referral link</a> and get $200 in free credit.</p>
<p>Already reading one of these books or have another favorite? Drop it in the comments, I'd love to check it out. Happy querying! 📊</p>
]]></description>
                                                            <category>mysql</category>
                                            <category>database</category>
                                            <category>sql</category>
                                            <category>postgres</category>
                                            <category>sqlite</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12922</guid>
                <pubDate>Sat, 29 Mar 2025 08:11:22 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Timeless Developer Skills: Building a Rock-Solid Foundation for Long-Term Success]]></title>
                <link>https://devdojo.com/bobbyiliev/timeless-developer-skills-building-a-rock-solid-foundation-for-long-term-success</link>
                <description><![CDATA[<p>As a developer who has worked with countless technologies over the years, I've realized something important: while frameworks and libraries come and go, certain fundamental skills remain invaluable throughout your career. Instead of constantly chasing the latest trend, mastering these timeless skills provides a strong foundation that will serve you regardless of industry changes.</p>
<h2 id="toc-1-algorithms-and-data-structures">1. ⚡ Algorithms and Data Structures</h2>
<h3 id="why-it-matters">Why It Matters</h3>
<p>Every piece of software processes data, and understanding how to efficiently organize and manipulate that data is essential. Mastering algorithms and data structures helps you develop a problem-solving mindset that transcends specific programming languages.</p>
<h3 id="how-to-learn">How to Learn</h3>
<ul>
<li>Implement fundamental structures like linked lists, stacks, and trees from scratch.</li>
<li>Solve classic problems on platforms like LeetCode and HackerRank, gradually increasing difficulty.</li>
<li>Analyze algorithms in your daily work - ask, "How would this scale with 10x more data?"</li>
<li>Build a small project that requires thoughtful data structure selection.</li>
<li>For more in-depth learning, check out the <a href="https://amzn.to/41N5AM8">Algorithms</a> book by Robert Sedgewick and Kevin Wayne</li>
<li>In addition to the above book, check out this GitHub repo: <a href="https://github.com/tayllan/awesome-algorithms">Awesome Algorithms</a></li>
</ul>
<h2 id="toc-2-system-design-and-scalability">2. 🏗️ System Design and Scalability</h2>
<h3 id="why-it-matters-1">Why It Matters</h3>
<p>The ability to design scalable systems separates junior developers from architects. Understanding system design ensures your applications won’t collapse under growing workloads.</p>
<h3 id="how-to-learn-1">How to Learn</h3>
<ul>
<li>Study the architecture of successful open-source projects.</li>
<li>Practice designing systems on paper before implementing them, focusing on bottlenecks.</li>
<li>Explore comprehensive resources like <a href="https://app.daily.dev/posts/system-design-the-complete-course-g81uw5ava">System Design: The Complete Course</a>.</li>
<li>Try scaling a simple app from handling 10 requests per second to 1000 and document what breaks first.</li>
</ul>
<h2 id="toc-3-version-control-and-git">3. 🔄 Version Control and Git</h2>
<h3 id="why-it-matters-2">Why It Matters</h3>
<p>Version control is more than just tracking changes - it enables collaboration, rollback safety, and a clear project history. Mastering Git can significantly improve your workflow.</p>
<h3 id="how-to-learn-2">How to Learn</h3>
<ul>
<li>Move beyond <code>commit</code>, <code>push</code>, and <code>pull</code> - learn how to use branches effectively.</li>
<li>Master Git’s advanced features like interactive rebasing, cherry-picking, and bisect.</li>
<li>Check out this free ebook: <a href="https://github.com/bobbyiliev/introduction-to-git-and-github-ebook">Introduction to Git and GitHub</a>.</li>
<li>Contribute to open-source projects to experience real-world Git collaboration.</li>
</ul>
<h2 id="toc-4-sql-and-database-design">4. 🗄️ SQL and Database Design</h2>
<h3 id="why-it-matters-3">Why It Matters</h3>
<p>Despite the rise of NoSQL databases, SQL remains the backbone of most applications. Understanding how to design efficient databases and optimize queries is an invaluable skill.</p>
<h3 id="how-to-learn-3">How to Learn</h3>
<ul>
<li>Design a schema for a real-world application, like an e-commerce platform.</li>
<li>Write increasingly complex queries, focusing on joins and aggregations.</li>
<li>Learn to read and interpret query execution plans for performance tuning.</li>
<li>Build a small CRUD application that emphasizes efficient database interactions.</li>
<li>Explore this free <a href="https://github.com/bobbyiliev/introduction-to-sql">Introduction to SQL</a> ebook.</li>
</ul>
<h2 id="toc-5-linux-and-shell-scripting">5. 🖥️ Linux and Shell Scripting</h2>
<h3 id="why-it-matters-4">Why It Matters</h3>
<p>Most servers, cloud instances, and containers run Linux. Being proficient with Linux commands and shell scripting improves productivity and troubleshooting abilities.</p>
<h3 id="how-to-learn-4">How to Learn</h3>
<ul>
<li>Set up a Linux environment for daily development.</li>
<li>Perform common operations via the terminal instead of relying on GUIs.</li>
<li>Automate repetitive tasks with simple Bash scripts.</li>
<li>Check out this <a href="https://github.com/bobbyiliev/introduction-to-bash-scripting">Introduction to Bash Scripting</a> ebook.</li>
<li>Dive deeper with this <a href="https://leanpub.com/introduction-to-linux">Introduction to Linux</a> book.</li>
</ul>
<h2 id="toc-6-networking">6. 🌍 Networking</h2>
<h3 id="why-it-matters-5">Why It Matters</h3>
<p>Understanding how data flows across networks helps with debugging, optimization, and building resilient applications.</p>
<h3 id="how-to-learn-5">How to Learn</h3>
<ul>
<li>Study the OSI model and where HTTP fits in.</li>
<li>Use tools like <code>curl</code>, Wireshark, and browser network debugging tools.</li>
<li>Build a simple REST API and analyze its network traffic.</li>
<li>Experiment with HTTP caching headers to observe their performance effects.</li>
<li>Check out this <a href="https://amzn.to/3FbiL0P">Networking Fundamentals</a> book.</li>
</ul>
<h2 id="toc-7-writing-clean-code-and-debugging">7. ✨ Writing Clean Code and Debugging</h2>
<h3 id="why-it-matters-6">Why It Matters</h3>
<p>Most development time is spent reading code, not writing it. Writing clean, readable code saves hours of maintenance and reduces bugs.</p>
<h3 id="how-to-learn-6">How to Learn</h3>
<ul>
<li>Review and refactor your old code for readability.</li>
<li>Master debugging tools specific to your tech stack.</li>
<li>Study design patterns but focus on when to apply them.</li>
<li>Practice pair programming to get immediate feedback on code clarity.</li>
<li>Check out the <a href="https://amzn.to/4kxoqOG">Clean Code</a> book by Robert C. Martin.</li>
</ul>
<h2 id="toc-8-security-best-practices">8. 🔐 Security Best Practices</h2>
<h3 id="why-it-matters-7">Why It Matters</h3>
<p>Security must be a priority, not an afterthought. Understanding vulnerabilities and how to prevent them is essential for protecting applications and user data.</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<h3 id="how-to-learn-7">How to Learn</h3>
<ul>
<li>Study the OWASP Top 10 vulnerabilities with real-world examples.</li>
<li>Set up a deliberately vulnerable app (like DVWA) and practice exploiting and fixing issues.</li>
<li>Integrate security checks into your development workflow.</li>
<li>Perform regular security reviews of your code and applications.</li>
<li>Check out Ross Anderson's <a href="https://www.cl.cam.ac.uk/~rja14/book.html">Security Engineering</a> book. It's free!</li>
</ul>
<h2 id="toc-9-cloud-and-infrastructure-as-code">9. ☁️ Cloud and Infrastructure as Code</h2>
<h3 id="why-it-matters-8">Why It Matters</h3>
<p>Infrastructure as Code (IaC) has revolutionized application deployment and scaling. Mastering these skills enables reproducible environments and eliminates configuration drift.</p>
<h3 id="how-to-learn-8">How to Learn</h3>
<ul>
<li>Start a project on AWS, GCP, or Azure using their free tiers.</li>
<li>Learn Docker fundamentals with this <a href="https://github.com/bobbyiliev/introduction-to-docker-ebook">Introduction to Docker</a>.</li>
<li>Practice automation with this <a href="https://leanpub.com/introduction-to-terraform">Introduction to Terraform</a> book.</li>
<li>Build a CI/CD pipeline for automated deployments.</li>
</ul>
<h2 id="toc-10-soft-skills-and-communication">10. 🗣️ Soft Skills and Communication</h2>
<h3 id="why-it-matters-9">Why It Matters</h3>
<p>Technical expertise alone isn’t enough - strong communication and collaboration skills accelerate career growth and make teamwork more effective.</p>
<h3 id="how-to-learn-9">How to Learn</h3>
<ul>
<li>Practice explaining technical concepts to non-technical people.</li>
<li>Contribute to open-source projects to experience diverse collaboration.</li>
<li>Write technical blog posts to improve written communication skills on platforms like <a href="https://dev.to/">DEV.to</a> and <a href="https://devdojo.com">DevDojo</a></li>
<li>Learn how to give and receive constructive feedback effectively.</li>
</ul>
<h2 id="structured-learning-paths">📍 Structured Learning Paths</h2>
<p>If you prefer structured guidance on skill development, check out <a href="https://roadmap.sh/">roadmap.sh</a>. Their curated roadmaps for different specializations complement the timeless skills in this guide.</p>
<h2 id="final-thoughts">🎯 Final Thoughts</h2>
<p>Developers who master these foundational skills adapt quickly to new technologies and frameworks. They recognize patterns over syntax, principles over commands, and solutions over isolated code snippets.</p>
<p>The key is to apply these skills through practical projects. Start small, build gradually, and learn from experienced developers. Mastery takes time, so focus on deep understanding and hands-on experience rather than surface-level knowledge.</p>
<h3 id="want-to-connect">📩 Want to Connect?</h3>
<p>If you have questions or want to share your learning journey, reach out to me on Twitter <a href="https://twitter.com/bobbyiliev_">@bobbyiliev_</a> or visit my blog at <a href="https://bobbyiliev.com">bobbyiliev.com</a>!</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>Need a server for practice? Get a <strong>free $200 credit on DigitalOcean</strong> with my referral link: <a href="https://m.do.co/c/2a9bba940f39">Free $200 Credit for DigitalOcean</a>. Perfect for experimenting with Linux, Docker, and networking!</p>
]]></description>
                                                            <category>devops</category>
                                            <category>webdev</category>
                                            <category>learning</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12857</guid>
                <pubDate>Sun, 02 Mar 2025 11:49:21 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to Build Windows Docker Images with GitHub Actions]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-build-windows-docker-images-with-github-actions</link>
                <description><![CDATA[<p>Have you ever needed to build a Windows Docker image but don't have access to a Windows machine? In this guide, I'll show you how to leverage GitHub Actions to build Windows container images without needing a local Windows environment.</p>
<h2 id="why-build-windows-docker-images">Why Build Windows Docker Images?</h2>
<p>While Linux containers are more common, Windows containers are essential for:</p>
<ul>
<li>.NET Framework applications</li>
<li>Windows-specific applications</li>
<li>Legacy Windows applications that need containerization</li>
<li>Applications that require Windows-specific features</li>
</ul>
<h2 id="prerequisites">Prerequisites</h2>
<p>Before we start, you'll need:</p>
<ul>
<li>A <a href="https://github.com">GitHub account</a></li>
<li>A <a href="https://hub.docker.com">Docker Hub account</a></li>
<li>A repository with your Dockerfile and application code</li>
<li>Docker Hub credentials configured as GitHub secrets</li>
</ul>
<p>You can take a look at my <a href="https://github.com/bobbyiliev/windows-docker-image-build">example repository</a> for reference.</p>
<h2 id="the-dockerfile">The Dockerfile</h2>
<p>Let's look at a sample Dockerfile that sets up a Python environment on Windows:</p>
<pre><code class="hljsruby"><span class="hljs-comment"># Use a more recent Windows Server Core image</span>
FROM mcr.microsoft.com/windows/<span class="hljs-symbol">servercore:</span>ltsc2022

<span class="hljs-comment"># Set shell to PowerShell</span>
SHELL [<span class="hljs-string">"powershell"</span>, <span class="hljs-string">"-Command"</span>, <span class="hljs-string">"$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"</span>]

<span class="hljs-comment"># Download and install Python</span>
RUN [Net.ServicePointManager]<span class="hljs-symbol">:</span><span class="hljs-symbol">:SecurityProtocol</span> = [Net.SecurityProtocolType]<span class="hljs-symbol">:</span><span class="hljs-symbol">:Tls12</span>; \
    Invoke-WebRequest -Uri <span class="hljs-symbol">https:</span>/<span class="hljs-regexp">/www.python.org/ftp</span><span class="hljs-regexp">/python/</span><span class="hljs-number">3.9</span>.<span class="hljs-number">5</span>/python-<span class="hljs-number">3.9</span>.<span class="hljs-number">5</span>-amd64.exe -OutFile python-<span class="hljs-number">3.9</span>.<span class="hljs-number">5</span>-amd64.exe ; \
    Start-Process python-<span class="hljs-number">3.9</span>.<span class="hljs-number">5</span>-amd64.exe -ArgumentList <span class="hljs-string">'/quiet InstallAllUsers=1 PrependPath=1'</span> -Wait ; \
    Remove-Item python-<span class="hljs-number">3.9</span>.<span class="hljs-number">5</span>-amd64.exe

<span class="hljs-comment"># Verify Python installation</span>
RUN python --version

<span class="hljs-comment"># Set the working directory</span>
WORKDIR /app

<span class="hljs-comment"># Copy application files</span>
COPY . /app

<span class="hljs-comment"># Set the startup command</span>
CMD [<span class="hljs-string">"python"</span>, <span class="hljs-string">"app.py"</span>]
</code></pre>
<p>This Dockerfile:</p>
<ul>
<li>Uses the <code>mcr.microsoft.com/windows/servercore:ltsc2022</code> base image</li>
<li>Installs Python 3.9.5</li>
<li>Sets the working directory to <code>/app</code></li>
<li>Copies the application files to the container</li>
<li>Specifies the startup command as <code>python app.py</code></li>
</ul>
<h2 id="setting-up-github-actions">Setting Up GitHub Actions</h2>
<p>Create a <code>.github/workflows/build.yml</code> file in your repository:</p>
<pre><code class="hljspowershell">name: Build Windows Docker Image

on:
  push:
    branches: [ <span class="hljs-type">main</span> ]
  workflow_dispatch:

jobs:
  build:
    runs<span class="hljs-literal">-on</span>: windows<span class="hljs-literal">-latest</span>
    environment: prod
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Login to Docker Hub
        uses: docker/login<span class="hljs-literal">-action</span>@v3
        with:
          username: <span class="hljs-variable">$</span>{{ secrets.DOCKERHUB_USERNAME }}
          password: <span class="hljs-variable">$</span>{{ secrets.DOCKERHUB_TOKEN }}

      - name: Build and Push
        shell: powershell
        run: |
          docker build . <span class="hljs-literal">-t</span> <span class="hljs-variable">$</span>{{ secrets.DOCKERHUB_USERNAME }}/windows<span class="hljs-literal">-python</span>:latest
          docker push <span class="hljs-variable">$</span>{{ secrets.DOCKERHUB_USERNAME }}/windows<span class="hljs-literal">-python</span>:latest
</code></pre>
<h2 id="how-it-works">How It Works</h2>
<ol>
<li>The workflow runs on <code>windows-latest</code> runner provided by GitHub Actions</li>
<li>It checks out your code</li>
<li>Logs into Docker Hub using your credentials</li>
<li>Builds the Windows Docker image</li>
<li>Pushes the image to Docker Hub</li>
<li>Displays the image digest for verification</li>
</ol>
<p>Note that in this example, for simplicity, we're using the <code>latest</code> tag. In a production environment, you should use versioned tags and instead of hardcoding the image name, you can use environment variables.</p>
<h2 id="important-considerations">Important Considerations</h2>
<ol>
<li><strong>Image Size</strong>: Windows containers are typically larger than Linux containers. Plan your registry storage accordingly.</li>
<li><strong>Build Time</strong>: Windows container builds usually take longer than Linux builds.</li>
<li><strong>Compatibility</strong>: Ensure your base image version matches your deployment environment.</li>
</ol>
<h2 id="resources">Resources</h2>
<p>Want to learn more about Docker? Check out these resources:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<ul>
<li><a href="https://devdojo.com/bobbyiliev/how-to-create-multi-platform-docker-images">How to Create Multi-Platform Docker Images</a></li>
<li><a href="https://github.com/bobbyiliev/introduction-to-docker-ebook">Free Docker eBook</a></li>
<li><a href="https://github.com/bobbyiliev/windows-docker-image-build">Example Repository</a></li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>GitHub Actions makes it possible to build Windows Docker images without a local Windows environment. This approach is particularly useful for cross-platform teams or developers primarily working on non-Windows systems.</p>
<p>If you're new to Docker and want to learn more, I've written a comprehensive <a href="https://github.com/bobbyiliev/introduction-to-docker-ebook">free Docker eBook</a> that covers all the basics and more.</p>
<p>Want to try this out but don't have a server? You can use my <a href="https://m.do.co/c/2a9bba940f39">DigitalOcean referral link</a> to get a free $200 credit!</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>Have questions or want to share your experience? Feel free to reach out to me on Twitter <a href="https://twitter.com/bobbyiliev_">@bobbyiliev_</a>.</p>
]]></description>
                                                            <category>git</category>
                                            <category>github</category>
                                            <category>docker</category>
                                            <category>windows</category>
                                            <category>devops</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12787</guid>
                <pubDate>Wed, 05 Feb 2025 10:11:49 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[Docker Networking - A Quick Guide to Get You Started]]></title>
                <link>https://devdojo.com/bobbyiliev/docker-networking-a-quick-guide-to-get-you-started</link>
                <description><![CDATA[<p>In this post, I'll show you how Docker networking works in simple terms. If you've ever wondered how containers talk to each other or connect to the internet, this guide is for you!</p>
<p>If you want to try this out but don't have a server, you can use my DigitalOcean referral link to get a free $200 credit: <a href="https://m.do.co/c/2a9bba940f39">Free $200 Credit For DigitalOcean</a>. This is more than enough to spin up a few Droplets and practice Docker networking!</p>
<p>And if you're new to Docker, I've written a free comprehensive Docker ebook that covers all the basics - you can check it out at <a href="https://github.com/bobbyiliev/introduction-to-docker-ebook">Introduction to Docker eBook</a>. The ebook will help you get started with Docker before diving into networking!</p>
<h2 id="docker-network-types">Docker Network Types</h2>
<p>Docker comes with 5 network types out of the box:</p>
<ol>
<li>Bridge (default)</li>
<li>Host</li>
<li>None</li>
<li>Overlay</li>
<li>MacVlan</li>
</ol>
<p>Check out the documentation for the network drivers <a href="https://docs.docker.com/engine/network/drivers/">here</a>.</p>
<p>Let's look at each type and see how they work in real-world scenarios!</p>
<h2 id="bridge-network-the-default-choice">Bridge Network - The Default Choice</h2>
<p>When you install Docker, it creates a default bridge network automatically. All containers use this network unless you tell them otherwise. Think of a bridge network like a virtual switch - it lets containers on the same network talk to each other.</p>
<p>Let's first check what networks we already have. Run this command:</p>
<pre><code class="hljs">docker network ls
</code></pre>
<p>You'll see something like this:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<pre><code class="hljsperl">NETWORK ID     NAME      DRIVER    SCOPE
abc123def456   bridge    bridge    <span class="hljs-keyword">local</span>
xyz789uvw321   host      host      <span class="hljs-keyword">local</span>
<span class="hljs-number">123</span>abc456def   none      null      <span class="hljs-keyword">local</span>
</code></pre>
<h3 id="creating-your-own-bridge-network">Creating Your Own Bridge Network</h3>
<p>While the default bridge network works, creating your own is better for a few reasons:</p>
<ul>
<li>Your containers can find each other by name (like 'laravel-app' or 'mysql-db')</li>
<li>You get better isolation from other containers on your system</li>
<li>You can connect and disconnect containers while they're running</li>
</ul>
<p>Here's how to create your own bridge network. We'll call it 'my-network':</p>
<pre><code class="hljsperl">docker network create <span class="hljs-keyword">my</span>-network
</code></pre>
<p>Now let's try something practical - we'll run an Nginx container and a test container to see if they can talk to each other:</p>
<pre><code class="hljsperl"><span class="hljs-comment"># Start a Nginx container</span>
docker run -d --name web --network <span class="hljs-keyword">my</span>-network nginx

<span class="hljs-comment"># Start an Ubuntu container</span>
docker run -it --name ubuntu --network <span class="hljs-keyword">my</span>-network ubuntu bash
</code></pre>
<p>From inside the Ubuntu container, you can now ping the Nginx container by name. This is super useful when setting up connections between your app and database:</p>
<pre><code class="hljs">ping web
</code></pre>
<h2 id="host-network-direct-access">Host Network - Direct Access</h2>
<p>The host network is different - it removes the network isolation between the container and your Docker host. It's like running the program directly on your machine. This can be useful for specific cases where you need the best network performance.</p>
<p>Here's how to use host networking:</p>
<pre><code class="hljs">docker run -d --network host nginx
</code></pre>
<p>Now Nginx is available directly on port 80 of your host - you don't need to map any ports!</p>
<blockquote>
<p>Warning: Be careful with host networking. Only use it when you really need to because it gives containers full access to your host's network.</p>
</blockquote>
<h2 id="port-mapping">Port Mapping</h2>
<p>When you're using bridge networking (which is most of the time), you need to map container ports to host ports if you want to access them from outside. Let me show you how this works:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">docker</span> <span class="hljs-selector-tag">run</span> <span class="hljs-selector-tag">-d</span> <span class="hljs-selector-tag">-p</span> 8080<span class="hljs-selector-pseudo">:80</span> <span class="hljs-selector-tag">nginx</span>
</code></pre>
<p>This command tells Docker: "Take port 80 inside the container and make it available as port 8080 on my host machine." Now you can access Nginx at <code>http://localhost:8080</code></p>
<h2 id="real-world-example-running-laravel-with-mysql">Real World Example: Running Laravel with MySQL</h2>
<p>Let's set up a more realistic example - a Laravel application with MySQL. We'll create a network for them to communicate and set up everything properly.</p>
<p>First, let's create a network for our Laravel app:</p>
<pre><code class="hljsruby"><span class="hljs-comment"># Create a network for our Laravel application</span>
docker network create laravel-net
</code></pre>
<p>Now, let's start MySQL. We'll give it a specific name so our Laravel app can find it:</p>
<pre><code class="hljsmarkdown"><span class="hljs-section"># Start MySQL with specific settings for Laravel</span>
docker run -d \
<span class="hljs-code">    --name mysql-db \</span>
<span class="hljs-code">    --network laravel-net \</span>
<span class="hljs-code">    -e MYSQL_ROOT_PASSWORD=secret \</span>
<span class="hljs-code">    -e MYSQL_DATABASE=laravel \</span>
<span class="hljs-code">    -e MYSQL_USER=laravel \</span>
<span class="hljs-code">    -e MYSQL_PASSWORD=secret \</span>
<span class="hljs-code">    mysql:5.7</span>
</code></pre>
<p>Let's break down what this does:</p>
<ul>
<li>Creates a container named 'mysql-db'</li>
<li>Connects it to our 'laravel-net' network</li>
<li>Sets up a root password</li>
<li>Creates a database called 'laravel'</li>
<li>Creates a user 'laravel' with password 'secret'</li>
</ul>
<p>Now for our Laravel application:</p>
<pre><code class="hljsruby"><span class="hljs-comment"># Start the Laravel application</span>
docker run -d \
    --name laravel-app \
    --network laravel-net \
    -p <span class="hljs-number">8000</span><span class="hljs-symbol">:</span><span class="hljs-number">80</span> \
    -e DB_HOST=mysql-db \
    -e DB_DATABASE=laravel \
    -e DB_USERNAME=laravel \
    -e DB_PASSWORD=secret \
    your-laravel-image
</code></pre>
<p>Here's what's happening:</p>
<ul>
<li>The app is named 'laravel-app'</li>
<li>It's connected to the same network as MySQL</li>
<li>Port 80 in the container is mapped to 8000 on your host</li>
<li>Environment variables are set to connect to MySQL</li>
<li>Notice we use 'mysql-db' as the database host - that's the container name!</li>
</ul>
<h2 id="checking-container-networks">Checking Container Networks</h2>
<p>Here are some helpful commands to check your container networks. I use these all the time when debugging:</p>
<ol>
<li>Want to see what network a container is using? Try this:</li>
</ol>
<pre><code class="hljspowershell">docker inspect <span class="hljs-operator">-f</span> <span class="hljs-string">'{{range $key, $value := .NetworkSettings.Networks}}{{$key}} {{end}}'</span> laravel<span class="hljs-literal">-app</span>
</code></pre>
<ol start="2">
<li>Need a list of all your networks?</li>
</ol>
<pre><code class="hljs">docker network ls
</code></pre>
<ol start="3">
<li>Want detailed info about a network? This shows all containers connected to it:</li>
</ol>
<pre><code class="hljs">docker network inspect laravel-net
</code></pre>
<h2 id="troubleshooting-tips">Troubleshooting Tips</h2>
<p>Here are some common issues and how to fix them:</p>
<ol>
<li>
<p>If your Laravel app can't connect to MySQL:</p>
<ul>
<li>Check if both containers are on the same network</li>
<li>Try <code>docker exec laravel-app ping mysql-db</code></li>
<li>Look at the environment variables in your Laravel app</li>
</ul>
</li>
<li>
<p>If you can't access your Laravel app from your browser:</p>
<ul>
<li>Check if the ports are mapped correctly with <code>docker ps</code></li>
<li>Make sure no other service is using port 8000 on your host</li>
<li>Try <code>curl localhost:8000</code> from your host machine</li>
</ul>
</li>
<li>
<p>Network seems slow?</p>
<ul>
<li>Consider using host networking for development</li>
<li>Check container logs for any error messages</li>
<li>Monitor container resources with <code>docker stats</code></li>
</ul>
</li>
</ol>
<h2 id="conclusion">Conclusion</h2>
<p>That's the basics of Docker networking! Remember these key points:</p>
<ul>
<li>Use bridge networks for connecting your containers</li>
<li>Create your own networks instead of using the default bridge</li>
<li>Use container names as hostnames when containers need to talk</li>
<li>Map ports when you need to access containers from outside</li>
</ul>
<p>Now you can set up proper networking for your Laravel applications in Docker. Try creating different networks and connecting containers to see how it all works together.</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>Have questions? You can reach me on Twitter <a href="https://twitter.com/bobbyiliev_">@bobbyiliev_</a> or check out my blog at <a href="https://bobbyiliev.com">bobbyiliev.com</a>!</p>
]]></description>
                                                            <category>docker</category>
                                            <category>linux</category>
                                            <category>devops</category>
                                            <category>networking</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12774</guid>
                <pubDate>Fri, 31 Jan 2025 06:29:58 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[5 Terraform Best Practices I Wish I Knew When I Started]]></title>
                <link>https://devdojo.com/bobbyiliev/5-terraform-best-practices-i-wish-i-knew-when-i-started</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Hey! I'm Bobby, a DevOps engineer and the author of the <a href="https://leanpub.com/introduction-to-terraform">Introduction to Terraform ebook</a>.</p>
<p>In this article, I'll share five Terraform best practices that I wish I knew when I first started using Terraform. These tips will help you write cleaner, more maintainable infrastructure as code and avoid common mistakes that can lead to deployment headaches.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>To follow along, you should have:</p>
<ul>
<li>Basic knowledge of Terraform</li>
<li>Terraform installed on your system</li>
</ul>
<p>If you're new to Terraform, I highly recommend checking out my <a href="https://leanpub.com/introduction-to-terraform">Introduction to Terraform ebook</a>, where I cover everything from the basics to managing infrastructure at scale.</p>
<hr>
<h2 id="step-1-always-use-remote-state-storage">Step 1 — Always Use Remote State Storage</h2>
<p>By default, Terraform stores its state file (<code>terraform.tfstate</code>) locally. However, in real-world projects, this is <strong>not</strong> ideal because:</p>
<ol>
<li>Local state files can be lost, leading to data inconsistencies.</li>
<li>Teams working together need a shared state to prevent conflicts.</li>
<li>Sensitive data may be exposed if the state file is not secured properly.</li>
</ol>
<p>A better approach is to store your state remotely using Terraform Cloud. S3, or HashiCorp Consul.</p>
<h3 id="example-storing-terraform-state-in-aws-s3">Example: Storing Terraform State in AWS S3</h3>
<pre><code class="hljsgo">terraform {
  backend <span class="hljs-string">"s3"</span> {
    bucket         = <span class="hljs-string">"my-terraform-state"</span>
    key            = <span class="hljs-string">"state/terraform.tfstate"</span>
    region         = <span class="hljs-string">"us-east-1"</span>
    encrypt        = <span class="hljs-literal">true</span>
    dynamodb_table = <span class="hljs-string">"terraform-lock"</span>
  }
}
</code></pre>
<p>This setup:</p>
<ul>
<li>Stores the state file in <strong>AWS S3</strong>.</li>
<li>Encrypts the state file for security.</li>
<li>Uses <strong>DynamoDB</strong> for state locking to prevent simultaneous updates.</li>
</ul>
<p>💡 <strong>Tip:</strong> If you're using Terraform Cloud, it provides a built-in remote state storage and locking mechanism.</p>
<hr>
<h2 id="step-2-use-modules-to-keep-code-dry">Step 2 — Use Modules to Keep Code DRY</h2>
<p>One of the biggest Terraform mistakes is copy-pasting 🍝 infrastructure code across different projects. Instead, you should use Terraform modules to keep your configurations reusable and maintainable.</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<p>For example, instead of writing the same code for every EC2 instance, you can create a reusable module:</p>
<pre><code class="hljs">modules/
  ec2-instance/
    main.tf
    variables.tf
    outputs.tf
</code></pre>
<h3 id="example-a-simple-ec2-module-main-tf">Example: A Simple EC2 Module (<code>main.tf</code>)</h3>
<pre><code class="hljsgo">resource <span class="hljs-string">"aws_instance"</span> <span class="hljs-string">"web"</span> {
  ami           = <span class="hljs-keyword">var</span>.ami_id
  instance_type = <span class="hljs-keyword">var</span>.instance_type
  tags = {
    Name = <span class="hljs-keyword">var</span>.name
  }
}
</code></pre>
<p>Then, in your main Terraform configuration, you can call the module:</p>
<pre><code class="hljsjavascript"><span class="hljs-built_in">module</span> <span class="hljs-string">"web_server"</span> {
  source        = <span class="hljs-string">"./modules/ec2-instance"</span>
  ami_id        = <span class="hljs-string">"ami-12345678"</span>
  instance_type = <span class="hljs-string">"t2.micro"</span>
  name          = <span class="hljs-string">"web-server"</span>
}
</code></pre>
<p>Benefits of using modules:</p>
<ul>
<li>Define infrastructure once and reuse it.</li>
<li>Modify a single module rather than updating multiple files.</li>
<li>Keeps your main configuration files clean.</li>
</ul>
<hr>
<h2 id="step-3-implement-terraform-workspaces-for-multi-environment-deployments">Step 3 — Implement Terraform Workspaces for Multi-Environment Deployments</h2>
<p>When managing multiple environments (e.g., development, staging, production), many people initially copy-paste Terraform files. This leads to configuration drift and inconsistency.</p>
<p>A better approach is to use Terraform workspaces, which allow you to manage multiple environments with the same Terraform code.</p>
<h3 id="example-switching-workspaces">Example: Switching Workspaces</h3>
<pre><code class="hljsgo">terraform workspace <span class="hljs-built_in">new</span> staging
terraform workspace list
terraform workspace <span class="hljs-keyword">select</span> staging
</code></pre>
<p>Inside your Terraform configuration, use <code>terraform.workspace</code>:</p>
<pre><code class="hljspowershell">resource <span class="hljs-string">"aws_s3_bucket"</span> <span class="hljs-string">"example"</span> {
  bucket = <span class="hljs-string">"my-app-<span class="hljs-variable">$</span>{terraform.workspace}"</span>
}
</code></pre>
<p>This automatically creates different S3 buckets based on the workspace (<code>my-app-dev</code>, <code>my-app-staging</code>, etc.).</p>
<p>💡 <strong>Tip:</strong> If you need more complex environment configurations, consider using <strong>separate state files</strong> rather than workspaces.</p>
<hr>
<h2 id="step-4-lock-provider-versions-to-avoid-unexpected-breakages">Step 4 — Lock Provider Versions to Avoid Unexpected Breakages</h2>
<p>Terraform providers are updated frequently, and sometimes these updates introduce breaking changes. If you don’t lock provider versions, your infrastructure might suddenly stop working when running <code>terraform apply</code>.</p>
<h3 id="example-locking-provider-versions">Example: Locking Provider Versions</h3>
<pre><code class="hljspowershell">terraform {
  required_providers {
    aws = {
      source  = <span class="hljs-string">"hashicorp/aws"</span>
      version = <span class="hljs-string">"~&gt; 5.0"</span> <span class="hljs-comment"># Locks to major version 5</span>
    }
  }

  required_version = <span class="hljs-string">"&gt;= 1.5.0"</span>
}
</code></pre>
<p>By doing this:</p>
<ul>
<li>You prevent unexpected changes when running Terraform commands.</li>
<li>Your infrastructure remains stable across deployments.</li>
</ul>
<p>💡 <strong>Tip:</strong> Always test updates in a separate branch before upgrading provider versions in production.</p>
<hr>
<h2 id="step-5-use-terraform-validate-and-format-before-applying-changes">Step 5 — Use Terraform Validate and Format Before Applying Changes</h2>
<p>Before running <code>terraform apply</code>, it's good practice to <strong>validate and format</strong> your Terraform configuration to catch issues early.</p>
<h3 id="example-checking-syntax-with-terraform-fmt-and-terraform-validate">Example: Checking Syntax with <code>terraform fmt</code> and <code>terraform validate</code></h3>
<pre><code class="hljspowershell">terraform fmt  <span class="hljs-comment"># Automatically formats code</span>
terraform validate  <span class="hljs-comment"># Checks for syntax errors</span>
</code></pre>
<p>You can also automate this in a CI/CD pipeline to ensure code consistency:</p>
<pre><code class="hljsmarkdown">name: Terraform CI

on: [push]

jobs:
  validate:
<span class="hljs-code">    runs-on: ubuntu-latest</span>
<span class="hljs-code">    steps:</span>
<span class="hljs-bullet">    - </span>uses: actions/checkout@v2
<span class="hljs-bullet">    - </span>name: Setup Terraform
<span class="hljs-code">      uses: hashicorp/setup-terraform@v1</span>
<span class="hljs-bullet">    - </span>name: Validate Terraform Code
<span class="hljs-code">      run: terraform validate</span>
</code></pre>
<p>Using these commands ensures:</p>
<ul>
<li>Your Terraform code follows best practices.</li>
<li>You catch issues <strong>before</strong> applying changes.</li>
<li>Your team follows a consistent formatting style.</li>
</ul>
<hr>
<h2 id="bonus-tip-keep-secrets-secure-with-environment-variables">Bonus Tip: Keep Secrets Secure with Environment Variables</h2>
<p>Terraform configurations often require API keys, passwords, and database credentials. Hardcoding secrets in your <code>.tf</code> files is a major security risk.</p>
<p>Instead, use environment variables or a secrets manager.</p>
<h3 id="example-using-environment-variables">Example: Using Environment Variables</h3>
<pre><code class="hljsjavascript"><span class="hljs-keyword">export</span> TF_VAR_db_password=<span class="hljs-string">"supersecretpassword"</span>
terraform apply
</code></pre>
<p>In Terraform:</p>
<pre><code class="hljsgo">variable <span class="hljs-string">"db_password"</span> {}

resource <span class="hljs-string">"aws_db_instance"</span> <span class="hljs-string">"example"</span> {
  password = <span class="hljs-keyword">var</span>.db_password
}
</code></pre>
<p>Better yet, use HashiCorp Vault or some other managed secrets managers for managing secrets securely.</p>
<hr>
<h2 id="conclusion">Conclusion</h2>
<p>These five best practices: using remote state, leveraging modules, implementing workspaces, locking provider versions, and validating Terraform code, will help you manage your infrastructure more efficiently.</p>
<p>If you're looking to dive deeper into Terraform, check out my ebook:</p>
<p>👉 <a href="https://leanpub.com/introduction-to-terraform">Introduction to Terraform</a></p>
<p>And if you're setting up your Terraform infrastructure on DigitalOcean, you can get <a href="https://m.do.co/c/2a9bba940f39">$200 in free credits</a> to get started!</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>Happy Terraforming! 🚀</p>
]]></description>
                                                            <category>linux</category>
                                            <category>cloud</category>
                                            <category>terraform</category>
                                            <category>devops</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12768</guid>
                <pubDate>Wed, 29 Jan 2025 07:06:23 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[7 Best Terraform Books for Beginners ]]></title>
                <link>https://devdojo.com/bobbyiliev/7-best-terraform-books-for-beginners</link>
                <description><![CDATA[<p>Terraform is a great tool for managing infrastructure as code, but getting started can be overwhelming without the right resources. Whether you're a developer, sysadmin, or cloud enthusiast, these beginner-friendly books and guides will help you take your first steps with Terraform.</p>
<p>I've included a mix of free and paid options to suit every learning style. Let's dive in!</p>
<hr>
<h3 id="toc-1-terraform-up-running-by-yevgeniy-brikman-paid"><strong>1. Terraform Up &amp; Running by Yevgeniy Brikman (Paid)</strong></h3>
<p>This is one of the most popular books for learning Terraform. It provides a comprehensive introduction to Terraform and progresses into advanced concepts like managing state, creating reusable modules, and scaling multi-cloud environments. The hands-on examples make it perfect for beginners and intermediate users.</p>
<p>👉 <a href="https://amzn.to/3WzSDCG">Get the book on Amazon</a></p>
<hr>
<h3 id="toc-2-introduction-to-terraform-paid"><strong>2. Introduction to Terraform (Paid)</strong></h3>
<p>This ebook is an excellent resource for developers, especially those working with DigitalOcean. It's beginner-friendly and focuses on using Terraform to manage infrastructure in a simple and cost-effective way. With clear examples for DigitalOcean users, it's perfect for learning how to automate and scale resources effortlessly.</p>
<p>Key benefits of learning with DigitalOcean:</p>
<ul>
<li><strong>Ease of use</strong>: Terraform and DigitalOcean are a great match for beginners.</li>
<li><strong>Affordability</strong>: DigitalOcean's low-cost infrastructure makes it ideal for experimentation.</li>
<li><strong>Practical examples</strong>: Includes step-by-step guides to create and manage resources like droplets and Kubernetes clusters.</li>
</ul>
<p>👉 <a href="https://leanpub.com/introduction-to-terraform">Get the ebook on Leanpub</a></p>
<hr>
<h3 id="toc-3-terraform-in-action-by-scott-winkler-paid"><strong>3. Terraform in Action by Scott Winkler (Paid)</strong></h3>
<p>This hands-on guide covers everything from Terraform basics to advanced topics like testing, CI/CD, and custom providers. It's a great resource for anyone looking to take their Terraform skills to the next level, with real-world examples that make complex concepts easier to grasp.</p>
<p>👉 <a href="https://amzn.to/3WV78Bh">Get the book on Amazon</a></p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<hr>
<h3 id="toc-4-hashicorp-s-terraform-documentation-free"><strong>4. HashiCorp's Terraform Documentation (Free)</strong></h3>
<p>While not a book, the official Terraform documentation is an incredible free resource. It's beginner-friendly and regularly updated, covering everything from basic usage to advanced features like modules, backends, and provider configuration.</p>
<p>👉 <a href="https://developer.hashicorp.com/terraform/docs">Check out the official docs</a></p>
<hr>
<h3 id="toc-5-the-terraform-book-by-james-turnbull-paid"><strong>5. The Terraform Book by James Turnbull (Paid)</strong></h3>
<p>This practical guide walks you through setting up your Terraform environment, creating reusable configurations, and managing infrastructure as code. It's straightforward and perfect for beginners who want to build a strong foundation in Terraform.</p>
<p>👉 <a href="https://amzn.to/42lWZku">Get the book on Amazon</a></p>
<hr>
<h3 id="toc-6-automating-multi-cloud-infrastructure-with-terraform-by-mikael-krief-paid"><strong>6. Automating Multi-Cloud Infrastructure with Terraform by Mikael Krief (Paid)</strong></h3>
<p>If you're interested in multi-cloud environments, this book is a must-read. It explains how to use Terraform to manage resources across AWS, Azure, and Google Cloud, while emphasizing best practices for scalability and maintainability.</p>
<p>👉 <a href="https://amzn.to/4h5qX0I">Get the book on Amazon</a></p>
<hr>
<h3 id="toc-7-terraform-best-practices-free"><strong>7. Terraform Best Practices (Free)</strong></h3>
<p>This free online resource is a must-read for anyone looking to adopt best practices when working with Terraform. Whether you're a beginner or an experienced user, this guide provides actionable advice to help you write clean, efficient, and maintainable Terraform code.</p>
<p>👉 <a href="https://www.terraform-best-practices.com/">Get the book on here</a></p>
<hr>
<h3 id="conclusion">Conclusion</h3>
<p>Terraform makes managing infrastructure simple and efficient, and the right resources can make learning it much easier. Whether you start with the comprehensive <em>Terraform Up &amp; Running</em>, dive into <em>Introduction to Terraform</em> for a DigitalOcean-focused approach, the key is to get started and experiment.</p>
<p>If you're looking for a cloud provider to practice with, DigitalOcean is a fantastic option. Use my <a href="https://m.do.co/c/2a9bba940f39">DigitalOcean referral link</a> to get $200 in free credit, perfect for building and testing your Terraform skills.</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>What are your favorite Terraform resources? Let me know in the comments below! 🌍✨</p>
]]></description>
                                                            <category>terraform</category>
                                            <category>devops</category>
                                            <category>ebooks</category>
                                            <category>iac</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12751</guid>
                <pubDate>Fri, 24 Jan 2025 06:42:47 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[9 Best Linux Books for Beginners]]></title>
                <link>https://devdojo.com/bobbyiliev/9-best-linux-books-for-beginners</link>
                <description><![CDATA[<p>Learning Linux can feel like a daunting task, especially if you're just starting out. But the right resources can make all the difference.</p>
<p>Whether you’re a developer, system administrator, or simply someone curious about Linux, these beginner-friendly books will guide you through the essentials. I've included a mix of free and paid options, so there’s something for everyone!</p>
<hr>
<h3 id="toc-1-101-linux-commands-free"><strong>1. 101 Linux Commands (Free)</strong></h3>
<p>This free ebook is perfect for Linux beginners who want to build a strong foundation. It covers 101 essential Linux commands with practical examples, making it a must-have guide for anyone getting started with the command line.</p>
<p>👉 <a href="https://github.com/bobbyiliev/101-linux-commands-ebook">Download the ebook here</a></p>
<hr>
<h3 id="toc-2-introduction-to-linux-paid"><strong>2. Introduction to Linux (Paid)</strong></h3>
<p>If you want to dive deeper into Linux basics, this ebook is for you. It covers the fundamentals of Linux, including installation, file systems, and permissions. It's ideal for beginners looking to build a solid understanding of Linux.</p>
<p>👉 <a href="https://leanpub.com/introduction-to-linux">Get the book on Leanpub</a></p>
<hr>
<h3 id="toc-3-introduction-to-bash-scripting-free"><strong>3. Introduction to Bash Scripting (Free)</strong></h3>
<p>Learning Linux often starts with learning Bash scripting. This free ebook covers the basics of Bash scripting, from writing your first script to automating tasks. It’s beginner-friendly and great for anyone looking to become more efficient with Linux.</p>
<p>👉 <a href="https://github.com/bobbyiliev/introduction-to-bash-scripting">Download the ebook here</a></p>
<hr>
<h3 id="toc-4-the-linux-command-line-by-william-shotts-free"><strong>4. The Linux Command Line by William Shotts (Free)</strong></h3>
<p>This classic guide is free to download and covers the Linux command line in detail. It starts with the basics and progresses to more advanced topics like scripting. Perfect for anyone who wants to become proficient in using the Linux terminal.</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<p>👉 <a href="https://linuxcommand.org/tlcl.php">Download it here</a></p>
<hr>
<h3 id="toc-5-how-linux-works-by-brian-ward-paid"><strong>5. How Linux Works by Brian Ward (Paid)</strong></h3>
<p>This highly recommended book explains the inner workings of Linux. It covers everything from boot processes to file systems and is an excellent resource for those who want to understand how Linux operates behind the scenes.</p>
<p>👉 <a href="https://amzn.to/4h6uJX3">Get the book on Amazon</a></p>
<hr>
<h3 id="toc-6-linux-for-beginners-by-jason-cannon-paid"><strong>6. Linux for Beginners by Jason Cannon (Paid)</strong></h3>
<p>Aimed at beginners, this book breaks down complex Linux concepts into easy-to-understand lessons. It covers basic commands, file management, and shell scripting, making it a great resource for those just starting out.</p>
<p>👉 <a href="https://amzn.to/3DYrlz6">Get the book on Amazon</a></p>
<hr>
<h3 id="toc-7-linux-bible-by-christopher-negus-paid"><strong>7. Linux Bible by Christopher Negus (Paid)</strong></h3>
<p>The <em>Linux Bible</em> is a comprehensive guide that’s great for beginners and intermediate users alike. It covers everything from basic commands to advanced system administration tasks. This book is a trusted resource for mastering Linux.</p>
<p>👉 <a href="https://amzn.to/3DJ2xLr">Get the book on Amazon</a></p>
<hr>
<h3 id="toc-8-gnu-linux-advanced-administration-by-remo-suppi-boldrito-free"><strong>8. GNU/Linux Advanced Administration by Remo Suppi Boldrito (Free)</strong></h3>
<p>This comprehensive guide is ideal for those who already have some Linux knowledge and want to level up their skills. It’s packed with practical tips and is freely available online.</p>
<p>👉 <a href="https://archive.org/details/ost-computer-science-fta-m2-admin_gnulinux-v1/page/n1/mode/2up">Download it here</a></p>
<hr>
<h3 id="toc-9-linux-official-documentation-free"><strong>9. Linux Official Documentation (Free)</strong></h3>
<p>Not exactly a book, but the Linux Documentation Project (LDP) offers free, high-quality guides for every skill level. From beginner tutorials to advanced topics, the LDP is a go-to resource for Linux learners.</p>
<p>👉 <a href="https://www.tldp.org/">Check out the documentation</a></p>
<hr>
<h3 id="conclusion"><strong>Conclusion</strong></h3>
<p>Linux is an incredibly powerful operating system, and with the right resources, anyone can learn to use it effectively. Whether you’re starting with free guides like <em>101 Linux Commands</em> and <em>The Linux Command Line</em> or diving into paid guides like <em>Linux Bible</em> or <em>How Linux Works</em>, there’s something here for every skill level.</p>
<p>Remember, the best way to learn Linux is by experimenting. If you need a server to test on, you can use my <a href="https://m.do.co/c/2a9bba940f39">DigitalOcean referral link</a> to get $200 in free credit, perfect for setting up a Linux environment.</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>Have you read any of these books or know of other great Linux resources? Let me know in the comments. Happy learning! 🐧</p>
]]></description>
                                                            <category>bash</category>
                                            <category>linux</category>
                                            <category>devops</category>
                                            <category>ebooks</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12718</guid>
                <pubDate>Wed, 08 Jan 2025 00:13:07 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[5 Best Docker Books for Beginners]]></title>
                <link>https://devdojo.com/bobbyiliev/5-best-docker-books-for-beginners</link>
                <description><![CDATA[<p>Getting started with Docker can feel overwhelming, but having the right resources makes all the difference.</p>
<p>Whether you're a developer, sysadmin, or just curious about containerization, these beginner-friendly books and guides will help you take your first steps with Docker.</p>
<p>I’ve included a mix of free and paid options to fit different budgets, so there’s something for everyone!</p>
<h3 id="toc-1-introduction-to-docker-free">1. Introduction to Docker (Free)</h3>
<p>This free ebook is written specifically for beginners and covers Docker from the ground up. It starts with the basics—like what containers are—and walks you through creating and managing your first Docker containers. Since it’s available as a free download on GitHub, it’s a great starting point for anyone exploring Docker.</p>
<p>👉 <a href="https://github.com/bobbyiliev/introduction-to-docker-ebook">Download the ebook here</a></p>
<h3 id="toc-2-the-docker-handbook-by-farhan-hasin-chowdhury-free">2. The Docker Handbook by Farhan Hasin Chowdhury (Free)</h3>
<p>Published by FreeCodeCamp, this is one of the best free resources for beginners. It’s straightforward and filled with practical examples. The book explains core Docker concepts and takes you through building and running containers, creating images, and even touches on Docker Compose.</p>
<p>👉 <a href="https://www.freecodecamp.org/news/the-docker-handbook/">Read it on FreeCodeCamp</a></p>
<h3 id="toc-3-docker-deep-dive-by-nigel-poulton-paid">3. Docker Deep Dive by Nigel Poulton (Paid)</h3>
<p>If you’re serious about learning Docker, this is one of the most comprehensive books available. Nigel Poulton explains Docker concepts in plain language and includes hands-on exercises to help you practice. Whether you’re learning about containers, volumes, or Docker Swarm, this book has you covered.</p>
<p>👉 <a href="https://amzn.to/4259Ozn">Get the book on Amazon</a></p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<h3 id="toc-4-the-docker-book-by-james-turnbull-paid">4. The Docker Book by James Turnbull (Paid)</h3>
<p>James Turnbull’s <em>The Docker Book</em> is a classic for Docker beginners. It offers a deep dive into the Docker ecosystem, from setting up your environment to deploying applications in containers. The book also introduces advanced concepts like networking and scaling containers.</p>
<p>👉 <a href="https://amzn.to/3PpRB82">Get the book on Amazon</a></p>
<h3 id="toc-5-docker-in-action-by-jeff-nickoloff-and-stephen-kuenzli-paid">5. Docker in Action by Jeff Nickoloff and Stephen Kuenzli (Paid)</h3>
<p>If you’re looking to go beyond the basics, <em>Docker in Action</em> is an excellent resource. The book combines beginner-friendly explanations with more advanced examples, like building CI/CD pipelines with Docker. It’s perfect for those who want to build practical Docker skills.</p>
<p>👉 <a href="https://amzn.to/3Ptm45s">Get the book on Amazon</a></p>
<h3 id="toc-6-docker-official-documentation-free">6. Docker Official Documentation (Free)</h3>
<p>Not exactly a book, but the Docker Docs are one of the best free resources available. They’re beginner-friendly, regularly updated, and include tutorials on everything from installation to advanced use cases. If you’re ever stuck, the official docs are a reliable place to look for answers.</p>
<p>👉 <a href="https://docs.docker.com/">Check out the Docker Docs</a></p>
<hr>
<h3 id="conclusion">Conclusion</h3>
<p>Docker is a game-changer for developers and sysadmins, and learning it is easier than ever with the right resources. Whether you prefer free guides like <em>Introduction to Docker</em> and <em>The Docker Handbook</em> or want a deeper dive with books like <em>Docker Deep Dive</em>, the key is to just start!</p>
<p>The best way to learn Docker is by doing. If you don’t have a server to experiment with, you can use my <a href="https://m.do.co/c/2a9bba940f39">DigitalOcean referral link</a> to get $200 in free credit. This is perfect for spinning up a droplet and trying out Docker in a real-world environment.</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>If you’ve read any of these or have other Docker books to recommend, let me know in the comments. Happy containerizing! 🚀</p>
]]></description>
                                                            <category>docker</category>
                                            <category>linux</category>
                                            <category>devops</category>
                                            <category>ebooks</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12590</guid>
                <pubDate>Wed, 27 Nov 2024 06:02:37 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[How To Consume an External API with Express.js]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-consume-an-external-api-with-expressjs</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>In this guide, we'll learn how to use <strong>Node.js</strong>, <strong>Express</strong>, and <strong>Neon Postgres</strong> to build a simple application that consumes the <strong>QuizAPI</strong> to fetch tech-related questions and store them in a PostgreSQL database.</p>
<p>By the end of this guide, you'll be able to:</p>
<ol>
<li>Use the <strong>axios</strong> library to make HTTP requests in Node.js.</li>
<li>Set up an <strong>Express</strong> server to handle routing.</li>
<li>Connect to a <strong>Neon Postgres</strong> database to store and retrieve data.</li>
<li>Deploy your application to a <strong>DigitalOcean Droplet</strong>.</li>
</ol>
<p>We'll even include storing the correct answers, so you can build on this project to create a full-fledged quiz application.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>Before starting, make sure you have:</p>
<ul>
<li><strong>Node.js</strong> and <strong>npm</strong> installed on your system. You can download them <a href="https://nodejs.org">here</a>.</li>
<li>A <strong>Neon Postgres</strong> database. Sign up for a free account <a href="https://neon.tech">here</a>.</li>
<li>A <strong>QuizAPI</strong> account with an API key. You can sign up <a href="https://quizapi.io">here</a>.</li>
<li>Optionally, a <strong>DigitalOcean Droplet</strong> for deployment. Use my affiliate link to get <strong>$200 in free credit</strong>: <a href="https://m.do.co/c/2a9bba940f39">DigitalOcean Free Credit</a>.</li>
</ul>
<p>If you're new to setting up a Node.js application on a server, check out this guide: <a href="https://devdojo.com/bobbyiliev/how-to-deploy-a-nodejs-app-on-digitalocean">How to Deploy a Node.js App on DigitalOcean</a>.</p>
<hr>
<h2 id="project-setup">Project Setup</h2>
<p>Let's get started by creating a new Node.js project and setting up the necessary dependencies.</p>
<h2 id="step-1-initialize-the-project">Step 1: Initialize the Project</h2>
<p>Open your terminal and create a new directory for your project:</p>
<pre><code class="hljsperl"><span class="hljs-keyword">mkdir</span> node-quiz-api
cd node-quiz-api
npm init -<span class="hljs-keyword">y</span>
</code></pre>
<p>The <code>-y</code> flag automatically fills in the default settings in your <code>package.json</code> file.</p>
<h2 id="step-2-install-dependencies">Step 2: Install Dependencies</h2>
<p>We need a few libraries to make our project work:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<pre><code class="hljs">npm install express axios pg dotenv
</code></pre>
<p>Here's a quick overview of the packages:</p>
<ul>
<li><strong>express</strong>: A minimalist web framework for Node.js.</li>
<li><strong>axios</strong>: A popular HTTP client for making API requests.</li>
<li><strong>pg</strong>: PostgreSQL client to connect to our Neon database.</li>
<li><strong>dotenv</strong>: Loads environment variables from a <code>.env</code> file.</li>
</ul>
<h2 id="step-3-setting-up-the-database">Step 3: Setting Up the Database</h2>
<p>We'll use <strong>Neon Postgres</strong> to store our quiz data. Make sure you’ve created your database and have the connection details ready.</p>
<h3 id="create-a-env-file">Create a <code>.env</code> File</h3>
<p>In the root of your project, create a file named <code>.env</code>:</p>
<pre><code class="hljsxml">DATABASE_URL=postgres://<span class="hljs-tag">&lt;<span class="hljs-name">username</span>&gt;</span>:<span class="hljs-tag">&lt;<span class="hljs-name">password</span>&gt;</span>@<span class="hljs-tag">&lt;<span class="hljs-name">host</span>&gt;</span>:<span class="hljs-tag">&lt;<span class="hljs-name">port</span>&gt;</span>/<span class="hljs-tag">&lt;<span class="hljs-name">database</span>&gt;</span>
QUIZ_API_KEY=YOUR_QUIZ_API_KEY
</code></pre>
<p>Replace <code>&lt;username&gt;</code>, <code>&lt;password&gt;</code>, <code>&lt;host&gt;</code>, <code>&lt;port&gt;</code>, and <code>&lt;database&gt;</code> with your actual Neon credentials. Don't forget to replace <code>YOUR_QUIZ_API_KEY</code> with your QuizAPI key.</p>
<h3 id="what-is-the-env-file">What is the <code>.env</code> file?</h3>
<p>The <code>.env</code> file allows you to securely store sensitive information like your database credentials and API keys. It prevents hardcoding these details in your code, reducing security risks.</p>
<hr>
<h2 id="step-4-creating-the-database-table">Step 4: Creating the Database Table</h2>
<p>To store questions and their answers, we'll create a table in our Neon database.</p>
<h3 id="connect-to-your-database">Connect to Your Database</h3>
<p>You can connect to your Neon Postgres database using a tool like <code>psql</code>, DBeaver, or any other Postgres client.</p>
<h3 id="create-the-questions-table">Create the <code>questions</code> Table</h3>
<p>Run the following SQL command:</p>
<pre><code class="hljsphp">CREATE TABLE questions (
    id SERIAL PRIMARY KEY,
    question TEXT NOT <span class="hljs-keyword">NULL</span>,
    answer_a TEXT,
    answer_b TEXT,
    answer_c TEXT,
    answer_d TEXT,
    correct_a BOOLEAN,
    correct_b BOOLEAN,
    correct_c BOOLEAN,
    correct_d BOOLEAN,
    created_at TIMESTAMP <span class="hljs-keyword">DEFAULT</span> CURRENT_TIMESTAMP
);
</code></pre>
<h3 id="explanation-of-the-table-structure">Explanation of the Table Structure:</h3>
<ul>
<li><code>id</code>: A unique identifier for each question.</li>
<li><code>question</code>: The quiz question text.</li>
<li><code>answer_a</code>, <code>answer_b</code>, <code>answer_c</code>, <code>answer_d</code>: Possible answer options.</li>
<li><code>correct_a</code>, <code>correct_b</code>, <code>correct_c</code>, <code>correct_d</code>: Boolean values indicating whether each answer is correct.</li>
<li><code>created_at</code>: A timestamp for when the question was added.</li>
</ul>
<hr>
<h2 id="step-5-setting-up-the-project-files">Step 5: Setting Up the Project Files</h2>
<p>Let's create the file structure for our application:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">node-quiz-api</span>
├── <span class="hljs-selector-class">.env</span>
├── <span class="hljs-selector-tag">app</span><span class="hljs-selector-class">.js</span>
├── <span class="hljs-selector-tag">db</span><span class="hljs-selector-class">.js</span>
└── <span class="hljs-selector-tag">package</span><span class="hljs-selector-class">.json</span>
</code></pre>
<h3 id="db-js"><code>db.js</code></h3>
<p>This file handles our connection to the Neon Postgres database:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">const</span> { Pool } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'pg'</span>);
<span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config();

<span class="hljs-keyword">const</span> pool = <span class="hljs-keyword">new</span> Pool({
  <span class="hljs-attr">connectionString</span>: process.env.DATABASE_URL,
  <span class="hljs-attr">ssl</span>: { <span class="hljs-attr">rejectUnauthorized</span>: <span class="hljs-literal">false</span> },
});

<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">query</span>: <span class="hljs-function">(<span class="hljs-params">text, params</span>) =&gt;</span> pool.query(text, params),
};
</code></pre>
<h3 id="explanation">Explanation:</h3>
<ul>
<li>We’re using the <code>pg</code> package to connect to our database.</li>
<li>The <code>Pool</code> class manages multiple connections efficiently.</li>
<li>We use <code>dotenv</code> to load environment variables from our <code>.env</code> file.</li>
</ul>
<hr>
<h3 id="app-js"><code>app.js</code></h3>
<p>This file sets up our Express server, handles requests to the QuizAPI, and stores the results in our database.</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">const</span> axios = <span class="hljs-built_in">require</span>(<span class="hljs-string">'axios'</span>);
<span class="hljs-keyword">const</span> db = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./db'</span>);
<span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config();

<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> PORT = process.env.PORT || <span class="hljs-number">3000</span>;

<span class="hljs-comment">// Fetch and store questions from QuizAPI</span>
app.get(<span class="hljs-string">'/fetch'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">'https://quizapi.io/api/v1/questions'</span>, {
      <span class="hljs-attr">params</span>: {
        <span class="hljs-attr">apiKey</span>: process.env.QUIZ_API_KEY,
        <span class="hljs-attr">limit</span>: <span class="hljs-number">10</span>,
      },
    });

    <span class="hljs-keyword">const</span> quizzes = response.data;

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> quiz <span class="hljs-keyword">of</span> quizzes) {
      <span class="hljs-keyword">const</span> { question, answers, correct_answers } = quiz;

      <span class="hljs-keyword">await</span> db.query(
        <span class="hljs-string">`INSERT INTO questions 
          (question, answer_a, answer_b, answer_c, answer_d, 
           correct_a, correct_b, correct_c, correct_d)
         VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`</span>,
        [
          question,
          answers.answer_a,
          answers.answer_b,
          answers.answer_c,
          answers.answer_d,
          correct_answers.answer_a_correct === <span class="hljs-string">'true'</span>,
          correct_answers.answer_b_correct === <span class="hljs-string">'true'</span>,
          correct_answers.answer_c_correct === <span class="hljs-string">'true'</span>,
          correct_answers.answer_d_correct === <span class="hljs-string">'true'</span>,
        ]
      );
    }

    res.send(<span class="hljs-string">'Questions fetched and stored successfully!'</span>);
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error:'</span>, error.message);
    res.status(<span class="hljs-number">500</span>).send(<span class="hljs-string">'Failed to fetch questions.'</span>);
  }
});

<span class="hljs-comment">// Retrieve all stored questions</span>
app.get(<span class="hljs-string">'/questions'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> db.query(<span class="hljs-string">'SELECT * FROM questions ORDER BY id DESC'</span>);
    res.json(result.rows);
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error:'</span>, error.message);
    res.status(<span class="hljs-number">500</span>).send(<span class="hljs-string">'Failed to fetch questions.'</span>);
  }
});

app.listen(PORT, () =&gt; {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server running on http://localhost:<span class="hljs-subst">${PORT}</span>`</span>);
});
</code></pre>
<h3 id="explanation-1">Explanation:</h3>
<ol>
<li><strong>Fetching Data</strong>: We use <code>axios</code> to make a request to the QuizAPI.</li>
<li><strong>Storing Data</strong>: We loop through the questions and store them in our Postgres database.</li>
<li><strong>Retrieving Data</strong>: The <code>/questions</code> endpoint returns all stored questions.</li>
</ol>
<hr>
<h2 id="step-6-running-the-application">Step 6: Running the Application</h2>
<p>To run your application:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">node</span> <span class="hljs-selector-tag">app</span><span class="hljs-selector-class">.js</span>
</code></pre>
<p>Visit:</p>
<ul>
<li><code>http://localhost:3000/fetch</code> to fetch and store questions.</li>
<li><code>http://localhost:3000/questions</code> to view the stored questions.</li>
</ul>
<hr>
<h2 id="step-7-deploying-your-application-to-digitalocean">Step 7: Deploying Your Application to DigitalOcean</h2>
<p>Now that your application is working locally, let's deploy it to a server so it's accessible to others. We'll use a <strong>DigitalOcean Droplet</strong> for this purpose.</p>
<p>If you're new to deploying Node.js applications, check out this comprehensive guide:<br>
<strong><a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-a-node-js-application-for-production-on-ubuntu-16-04">How To Set Up a Node.js Application for Production on Ubuntu 16.04</a></strong>.</p>
<h3 id="quick-deployment-steps">Quick Deployment Steps:</h3>
<ol>
<li>
<p><strong>Create a Droplet</strong>: Use my <a href="https://m.do.co/c/2a9bba940f39">DigitalOcean link</a> to get <strong>$200 in free credit</strong> and spin up an Ubuntu Droplet.</p>
</li>
<li>
<p><strong>SSH into Your Droplet</strong>:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">ssh</span> <span class="hljs-selector-tag">root</span><span class="hljs-keyword">@your_droplet_ip</span>
</code></pre>
</li>
<li>
<p><strong>Install Node.js</strong>:</p>
<p>Follow the steps in the linked guide to install Node.js, Nginx, and set up a firewall.</p>
</li>
<li>
<p><strong>Clone Your Project</strong>:</p>
<pre><code class="hljsphp">git <span class="hljs-keyword">clone</span> https:<span class="hljs-comment">//github.com/your-username/node-quiz-api.git</span>
cd node-quiz-api
</code></pre>
</li>
<li>
<p><strong>Install Dependencies</strong>:</p>
<pre><code class="hljs">npm install
</code></pre>
</li>
<li>
<p><strong>Set Up Environment Variables</strong>:</p>
<p>Create a <code>.env</code> file on the server and copy your local <code>.env</code> content into it.</p>
</li>
<li>
<p><strong>Start Your Application</strong>:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">node</span> <span class="hljs-selector-tag">app</span><span class="hljs-selector-class">.js</span>
</code></pre>
</li>
<li>
<p><strong>Use PM2 for Process Management</strong>:</p>
<p>To keep your Node.js app running in the background, install <code>pm2</code>:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">npm</span> <span class="hljs-selector-tag">install</span> <span class="hljs-selector-tag">-g</span> <span class="hljs-selector-tag">pm2</span>
<span class="hljs-selector-tag">pm2</span> <span class="hljs-selector-tag">start</span> <span class="hljs-selector-tag">app</span><span class="hljs-selector-class">.js</span>
</code></pre>
</li>
<li>
<p><strong>Set Up Nginx</strong>:</p>
<p>Use Nginx as a reverse proxy to route incoming requests to your Node.js app. Follow the configuration steps in the <a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-a-node-js-application-for-production-on-ubuntu-16-04">DigitalOcean guide</a>.</p>
</li>
<li>
<p><strong>Access Your App</strong>:</p>
<p>Once everything is set up, you can access your application via your server's IP address.</p>
</li>
</ol>
<p>Use my <a href="https://m.do.co/c/2a9bba940f39">DigitalOcean link</a> to get $200 in free credit!</p>
<hr>
<h2 id="conclusion">Conclusion</h2>
<p>Here is how our Node.js application consumes the QuizAPI and stores the data in the Neon Postgres database:</p>
<pre><code class="hljsruby">                +------------------------------------+
                <span class="hljs-params">|          QuizAPI Service           |</span>
                <span class="hljs-params">| https://quizapi.io/api/v1/questions|</span>
                +------------------------------------+
                                ^
                                <span class="hljs-params">|
                      (HTTP GET Request)
                                |</span>
                                v
   +------------------------------------------------------+
   <span class="hljs-params">|                    Node.js Application               |</span>
   <span class="hljs-params">|------------------------------------------------------|</span>
   <span class="hljs-params">| 1. Express server receives request at '/fetch' route |</span>
   <span class="hljs-params">| 2. Uses Axios to fetch questions from QuizAPI        |</span>
   <span class="hljs-params">| 3. Processes the response data                       |</span>
   <span class="hljs-params">| 4. Stores questions <span class="hljs-keyword">and</span> answers <span class="hljs-keyword">in</span> the database      |</span>
   +------------------------------------------------------+
                                <span class="hljs-params">|
                                |</span>
                      (Database Query)
                                <span class="hljs-params">|
                                v
        +----------------------------------------+
        |</span>         Neon Postgres Database          <span class="hljs-params">|
        |</span>----------------------------------------<span class="hljs-params">|
        |</span> <span class="hljs-symbol">Stores:</span>                                <span class="hljs-params">|
        |</span> - Question text                        <span class="hljs-params">|
        |</span> - Answer options (A, B, C, D)          <span class="hljs-params">|
        |</span> - Correct answer flags (<span class="hljs-literal">true</span>/<span class="hljs-literal">false</span>)    <span class="hljs-params">|
        +----------------------------------------+
                                ^
                                |</span>
                      (Database Query)
                                <span class="hljs-params">|
                                v
   +----------------------------------------------------------+
   |</span>  Node.js Application retrieves questions via <span class="hljs-string">'/questions'</span><span class="hljs-params">|
   +----------------------------------------------------------+
                                |</span>
                                <span class="hljs-params">|
                                v
                  +-------------------------------+
                  |</span>       Client Browser          <span class="hljs-params">|
                  |</span>   (Displays stored data)      <span class="hljs-params">|
                  +-------------------------------+
</span></code></pre>
<p>With that we've built a Node.js application that fetches data from an external API and stores it in a Neon Postgres database. You can now extend this project by adding a frontend, implementing authentication, or even creating a full quiz game.</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
]]></description>
                                                            <category>javascript</category>
                                            <category>nodejs</category>
                                            <category>api</category>
                                            <category>quizapi</category>
                                            <category>webdev</category>
                                            <category>restapi</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12552</guid>
                <pubDate>Mon, 18 Nov 2024 06:13:26 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to Create Multi-Platform Docker Images]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-create-multi-platform-docker-images</link>
                <description><![CDATA[<p>Building Docker images that work on different CPU architectures like ARM and x86 is becoming more important, especially with the rise of ARM-based machines like M1/M2 Macs and Raspberry Pis. In this guide, I'll show you how to build images that run everywhere!</p>
<h2 id="what-are-multi-platform-images">What are Multi-Platform Images?</h2>
<p>Multi-platform images are Docker images that can run on different CPU architectures. Instead of building separate images for each architecture, we can create a single image that works across:</p>
<ul>
<li>AMD64 (regular Intel/AMD computers)</li>
<li>ARM64 (M1/M2 Macs, newer Raspberry Pis)</li>
<li>ARMv7 (older Raspberry Pis)</li>
<li>And more!</li>
</ul>
<h2 id="prerequisites">Prerequisites</h2>
<p>Before we start, you'll need:</p>
<ul>
<li>Docker installed on your machine</li>
<li>Docker BuildKit enabled (it's enabled by default in newer Docker versions)</li>
<li>Docker Hub account (or another registry) to push your images</li>
</ul>
<h2 id="setting-up-buildx">Setting Up BuildX</h2>
<p>BuildX is Docker's tool for building multi-platform images. Let's set it up:</p>
<ol>
<li>Create a new builder instance:</li>
</ol>
<pre><code class="hljsperl">docker buildx create --name mybuilder --<span class="hljs-keyword">use</span>
</code></pre>
<ol start="2">
<li>Start the builder:</li>
</ol>
<pre><code class="hljs">docker buildx inspect --bootstrap
</code></pre>
<h2 id="creating-a-simple-multi-platform-image">Creating a Simple Multi-Platform Image</h2>
<p>Let's create a basic web server as an example. First, make a new directory:</p>
<pre><code class="hljsperl"><span class="hljs-keyword">mkdir</span> multi-arch-demo
cd multi-arch-demo
</code></pre>
<p>Create a simple <code>index.html</code>:</p>
<pre><code class="hljsxml">echo "<span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Hello from $(uname -m)<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>" &gt; index.html
</code></pre>
<p>Now create a <code>Dockerfile</code>:</p>
<pre><code class="hljs">FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
</code></pre>
<h2 id="building-for-multiple-platforms">Building for Multiple Platforms</h2>
<p>Here's the cool part! Build and push the image for multiple platforms in one command:</p>
<pre><code class="hljsperl">docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 \
  -t yourusername/multi-arch-demo:latest \
  --<span class="hljs-keyword">push</span> .
</code></pre>
<p>This command:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<ul>
<li>Builds for three different architectures</li>
<li>Tags the image with your username</li>
<li>Pushes it to Docker Hub</li>
</ul>
<h2 id="testing-your-multi-platform-image">Testing Your Multi-Platform Image</h2>
<p>To test your image, you can pull and run it on different devices:</p>
<pre><code class="hljsruby"><span class="hljs-comment"># On a regular computer (AMD64)</span>
docker run -p <span class="hljs-number">80</span><span class="hljs-symbol">:</span><span class="hljs-number">80</span> yourusername/multi-arch-<span class="hljs-symbol">demo:</span>latest

<span class="hljs-comment"># On a Raspberry Pi (ARM)</span>
docker run -p <span class="hljs-number">80</span><span class="hljs-symbol">:</span><span class="hljs-number">80</span> yourusername/multi-arch-<span class="hljs-symbol">demo:</span>latest

<span class="hljs-comment"># On an M1/M2 Mac (ARM64)</span>
docker run -p <span class="hljs-number">80</span><span class="hljs-symbol">:</span><span class="hljs-number">80</span> yourusername/multi-arch-<span class="hljs-symbol">demo:</span>latest
</code></pre>
<p>Docker automatically picks the right version for each device!</p>
<h2 id="real-world-example-python-application">Real-World Example: Python Application</h2>
<p>Let's create a more practical example with a Python application:</p>
<p>Create a simple <code>app.py</code>:</p>
<pre><code class="hljspython"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask
<span class="hljs-keyword">import</span> platform

app = Flask(__name__)

<span class="hljs-meta">@app.route('/')</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">hello</span><span class="hljs-params">()</span>:</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">f"Hello from <span class="hljs-subst">{platform.machine()}</span>!"</span>

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    app.run(host=<span class="hljs-string">'0.0.0.0'</span>, port=<span class="hljs-number">5000</span>)
</code></pre>
<p>Create a <code>requirements.txt</code>:</p>
<pre><code class="hljs">flask==2.0.1
</code></pre>
<p>Create a multi-platform <code>Dockerfile</code>:</p>
<pre><code class="hljspowershell">FROM -<span class="hljs-literal">-platform</span>=<span class="hljs-variable">$BUILDPLATFORM</span> python:<span class="hljs-number">3.9</span><span class="hljs-literal">-slim</span>

WORKDIR /app
COPY requirements.txt .
RUN pip install <span class="hljs-literal">-r</span> requirements.txt

COPY app.py .
EXPOSE <span class="hljs-number">5000</span>

CMD [<span class="hljs-string">"python"</span>, <span class="hljs-string">"app.py"</span>]
</code></pre>
<p>Build and push for multiple platforms:</p>
<pre><code class="hljsperl">docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 \
  -t yourusername/python-multi-arch:latest \
  --<span class="hljs-keyword">push</span> .
</code></pre>
<h2 id="tips-and-tricks">Tips and Tricks</h2>
<ol>
<li><strong>Cache Management</strong></li>
</ol>
<pre><code class="hljsperl">docker buildx build --platform linux/amd64,linux/arm64 \
  --cache-from type=registry,<span class="hljs-keyword">ref</span>=yourusername/myapp:cache \
  --cache-to type=registry,<span class="hljs-keyword">ref</span>=yourusername/myapp:cache \
  -t yourusername/myapp:latest \
  --<span class="hljs-keyword">push</span> .
</code></pre>
<ol start="2">
<li><strong>Checking Available Platforms</strong></li>
</ol>
<pre><code class="hljs">docker buildx ls
</code></pre>
<ol start="3">
<li><strong>Inspecting Multi-Platform Images</strong></li>
</ol>
<pre><code class="hljs">docker buildx imagetools inspect yourusername/multi-arch-demo:latest
</code></pre>
<h2 id="common-issues-and-solutions">Common Issues and Solutions</h2>
<ol>
<li><strong>No space left on device</strong>
Solution: Clean up your builder:</li>
</ol>
<pre><code class="hljs">docker buildx prune
</code></pre>
<ol start="2">
<li><strong>QEMU errors</strong>
Solution: Install QEMU support:</li>
</ol>
<pre><code class="hljs">docker run --privileged --rm tonistiigi/binfmt --install all
</code></pre>
<ol start="3">
<li><strong>Build taking too long</strong>
Solution: Build only the platforms you need:</li>
</ol>
<pre><code class="hljsperl">docker buildx build --platform linux/amd64,linux/arm64 \
  -t yourusername/myapp:latest \
  --<span class="hljs-keyword">push</span> .
</code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>Multi-platform Docker images are super helpful when you need your application to run on different types of computers. With BuildX, it's pretty straightforward to create images that work everywhere. The key things to remember are:</p>
<ol>
<li>Use BuildX for multi-platform builds</li>
<li>Test your images on different architectures</li>
<li>Only build for the platforms you actually need</li>
<li>Keep your images small and efficient</li>
</ol>
<p>Now you can build Docker images that run on any device, from your laptop to a Raspberry Pi!</p>
<p>If you want to try this out but don't have a server, you can use my DigitalOcean referral link to get a free $200 credit: <a href="https://m.do.co/c/2a9bba940f39">Free $200 Credit For DigitalOcean</a>. This is more than enough to spin up a few Droplets and practice building multi-architecture Docker images!</p>
<p>And if you're new to Docker, I've written a free Docker ebook that covers all the basics - you can check it out at <a href="https://github.com/bobbyiliev/introduction-to-docker-ebook">Introduction to Docker eBook</a>. The ebook will help you get started with Docker before diving into multi-architecture builds!</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>Give it a try and let me know if you have any questions. You can reach me on Twitter <a href="https://twitter.com/bobbyiliev_">@bobbyiliev_</a>.</p>
]]></description>
                                                            <category>docker</category>
                                            <category>linux</category>
                                            <category>devops</category>
                                            <category>ci-cd</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12551</guid>
                <pubDate>Mon, 18 Nov 2024 05:29:25 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[How To Use Pan for Easy Analytics in Your Laravel App]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-use-pan-for-easy-analytics-in-your-laravel-app</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>When you're building a website or an app, knowing how users interact with it is key to improving the experience. However, many tracking tools are complicated, costly, or don't care about privacy. That's where <strong>Pan</strong> steps in. Pan is a simple, privacy-friendly tool for tracking user behavior on your Laravel app, created by Nuno Maduro and David Hill from the Laravel team.</p>
<p>In this guide, I'll show you how to integrate Pan into your Laravel project and track things like views, hovers, and clicks without collecting personal data from your users.</p>
<h2 id="what-you-ll-need">What You'll Need</h2>
<p>Before we jump in, make sure you have the following ready:</p>
<ul>
<li>PHP 8.3 or higher</li>
<li>A Laravel 11+ project set up</li>
<li>Composer installed (helps manage your project dependencies)</li>
</ul>
<p>Check out this intro video by <a href="https://x.com/joshcirre">Josh Cirre</a>:</p>
<p><iframe width="100%" height="399" src="https://www.youtube.com/embed/JFtyJvKoPEY" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
<h2 id="step-1-installing-pan">Step 1 — Installing Pan</h2>
<p>First up, let's add Pan to your Laravel project. You’ll do this using Composer:</p>
<ol>
<li>Open your terminal.</li>
<li>Navigate to your Laravel project folder.</li>
<li>Run this command:</li>
</ol>
<pre><code class="hljsjavascript">composer <span class="hljs-built_in">require</span> panphp/pan
</code></pre>
<p>This will install Pan into your project.</p>
<ol start="4">
<li>Now, run this command to set up Pan:</li>
</ol>
<pre><code class="hljscss"><span class="hljs-selector-tag">php</span> <span class="hljs-selector-tag">artisan</span> <span class="hljs-selector-tag">install</span><span class="hljs-selector-pseudo">:pan</span>
</code></pre>
<p>This command will configure everything for you!</p>
<h2 id="step-2-track-user-actions">Step 2 — Track User Actions</h2>
<p>With Pan installed, you can now track parts of your website. Pan uses a <code>data-pan</code> attribute to track specific elements.</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<p>For example, to track when someone clicks or hovers over a "Sign Up" button, you’d add this:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">data-pan</span>=<span class="hljs-string">"sign-up-button"</span>&gt;</span>Sign Up<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>Here are more examples:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">data-pan</span>=<span class="hljs-string">"hero-section"</span>&gt;</span>Welcome to our site!<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">data-pan</span>=<span class="hljs-string">"learn-more-link"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/about"</span>&gt;</span>Learn More<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">data-pan</span>=<span class="hljs-string">"contact-form"</span>&gt;</span>
  <span class="hljs-comment">&lt;!-- Your form fields here --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</code></pre>
<p>Pan will automatically start tracking impressions, hovers, and clicks for these elements!</p>
<h2 id="step-3-view-your-tracking-data">Step 3 — View Your Tracking Data</h2>
<p>Now that Pan is tracking your elements, you can view the data it collects using Artisan:</p>
<ol>
<li>Open your terminal.</li>
<li>Go to your project folder.</li>
<li>Run this command:</li>
</ol>
<pre><code class="hljs">php artisan pan
</code></pre>
<p>You'll get a table showing how many times each element has been viewed, hovered over, or clicked.</p>
<p>If you want to check a specific element, use:</p>
<pre><code class="hljs">php artisan pan --filter=sign-up-button
</code></pre>
<p>Replace <code>sign-up-button</code> with the element you’re tracking.</p>
<h2 id="step-4-customize-pan-optional">Step 4 — Customize Pan (Optional)</h2>
<p>By default, Pan tracks up to 50 elements, but you can change this if needed. To customize Pan, open the <code>AppServiceProvider.php</code> file and add:</p>
<pre><code class="hljsphp"><span class="hljs-keyword">use</span> <span class="hljs-title">Pan</span>\<span class="hljs-title">PanConfiguration</span>;

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">register</span><span class="hljs-params">()</span>: <span class="hljs-title">void</span>
</span>{
    <span class="hljs-comment">// Track specific elements</span>
    PanConfiguration::allowedAnalytics([
        <span class="hljs-string">'sign-up-button'</span>,
        <span class="hljs-string">'contact-form'</span>,
    ]);

    <span class="hljs-comment">// Or increase the limit</span>
    <span class="hljs-comment">// PanConfiguration::maxAnalytics(10000);</span>

    <span class="hljs-comment">// Or remove the limit entirely</span>
    <span class="hljs-comment">// PanConfiguration::unlimitedAnalytics();</span>
}
</code></pre>
<h2 id="step-5-what-pan-tracks">Step 5 — What Pan Tracks</h2>
<p>Here’s what Pan tracks:</p>
<ol>
<li><strong>Impressions</strong>: Counts how many times an element is seen.</li>
<li><strong>Hovers</strong>: Counts how many times users hover over an element.</li>
<li><strong>Clicks</strong>: Counts how many times users click on an element.</li>
</ol>
<p>Pan is completely privacy-friendly—it doesn’t collect any personal data about your users.</p>
<h2 id="step-6-use-the-data-to-improve">Step 6 — Use the Data to Improve</h2>
<p>With Pan tracking how users interact with your site, you can use that data to improve your site. For example:</p>
<ul>
<li>If a button isn’t clicked much, maybe it needs more attention or clearer text.</li>
<li>If users are hovering over an element but not clicking, maybe it’s confusing.</li>
<li>If some sections aren’t being viewed, think about moving them to more visible areas.</li>
</ul>
<h2 id="step-7-clear-tracking-data">Step 7 — Clear Tracking Data</h2>
<p>If you ever need to clear Pan's tracking data, it’s easy:</p>
<ol>
<li>Open your terminal.</li>
<li>Navigate to your project folder.</li>
<li>Run this command:</li>
</ol>
<pre><code class="hljscss"><span class="hljs-selector-tag">php</span> <span class="hljs-selector-tag">artisan</span> <span class="hljs-selector-tag">pan</span><span class="hljs-selector-pseudo">:flush</span>
</code></pre>
<p>This will reset Pan's tracking data, so you can start fresh.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Pan makes it easy to understand how users interact with your Laravel app without compromising their privacy.</p>
<p>By adding the <code>data-pan</code> attribute and using Artisan commands, you can track user behavior and make informed improvements to your website or app.</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
]]></description>
                                                            <category>php</category>
                                            <category>laravel</category>
                                            <category>webdev</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12442</guid>
                <pubDate>Thu, 17 Oct 2024 05:19:59 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Lockdown Your Containers: 11 Essential Docker Security Tips]]></title>
                <link>https://devdojo.com/bobbyiliev/lockdown-your-containers-11-essential-docker-security-tips</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Hey! I'm Bobby, a Docker Captain and the author of the <a href="https://devdojo.com/bobbyiliev/free-introduction-to-docker-ebook">free Introduction to Docker eBook</a>. In this article, we'll cover 11 essential Docker security tips to help you protect your containerized applications.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>To follow this guide, you should have:</p>
<ul>
<li>Docker installed on your system</li>
<li>Basic knowledge of Docker commands and concepts</li>
</ul>
<h2 id="toc-1-keep-docker-updated">1. Keep Docker Updated</h2>
<p>Regularly updating your Docker engine is crucial for maintaining the security of your containerized environment. Each new release of Docker often includes security patches and fixes for vulnerabilities discovered in previous versions.</p>
<p>To update Docker on a Ubuntu system, you can use the following commands:</p>
<pre><code class="hljsjavascript">sudo apt-<span class="hljs-keyword">get</span> update
sudo apt-<span class="hljs-keyword">get</span> upgrade docker-ce
</code></pre>
<p>After updating, it's a good practice to restart the Docker daemon:</p>
<pre><code class="hljs">sudo systemctl restart docker
</code></pre>
<p>Make sure to test your applications after updating Docker to ensure compatibility with the new version.</p>
<h2 id="toc-2-use-official-images">2. Use Official Images</h2>
<p><a href="https://docs.docker.com/trusted-content/official-images/">Official images from Docker Hub</a> are maintained by the Docker team and the original software maintainers. They follow best practices for Docker image creation and are regularly updated with security patches.</p>
<p>When using official images in your <code>docker-compose.yml</code> file, always specify a specific version tag rather than using <code>latest</code>:</p>
<pre><code class="hljsruby"><span class="hljs-symbol">version:</span> <span class="hljs-string">'3.8'</span>
<span class="hljs-symbol">services:</span>
  <span class="hljs-symbol">web:</span>
    <span class="hljs-symbol">image:</span> <span class="hljs-symbol">nginx:</span><span class="hljs-number">1.21</span>.<span class="hljs-number">3</span>  <span class="hljs-comment"># Specific version of the official Nginx image</span>
</code></pre>
<p>Using specific version tags ensures reproducibility and prevents unexpected changes when rebuilding your containers.</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<h2 id="toc-3-scan-images-for-vulnerabilities">3. Scan Images for Vulnerabilities</h2>
<p>Regularly scanning your Docker images for vulnerabilities is essential for maintaining a secure environment. <a href="https://docs.docker.com/scout/">Docker Scout</a> is a powerful tool integrated into Docker Desktop and the Docker CLI for this purpose.</p>
<p>To scan an image using Docker Scout:</p>
<pre><code class="hljsruby">docker scout cve &lt;image_name&gt;<span class="hljs-symbol">:&lt;tag&gt;</span>
</code></pre>
<p>For example:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">docker</span> <span class="hljs-selector-tag">scout</span> <span class="hljs-selector-tag">cve</span> <span class="hljs-selector-tag">nginx</span><span class="hljs-selector-pseudo">:1.21.3</span>
</code></pre>
<p>This command will provide a detailed report of any known vulnerabilities in the image, including their severity levels and available fixes.</p>
<p>You can also integrate Docker Scout into your CI/CD pipeline. Here's an example of how you might do this in a GitHub Actions workflow:</p>
<pre><code class="hljsperl">name: Docker Image CI

on: [<span class="hljs-keyword">push</span>]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Build the Docker image
      run: docker build . --file Dockerfile --tag myapp:${GITHUB_SHA}
    - name: Scan the Docker image
      run: docker scout cve myapp:${GITHUB_SHA}
</code></pre>
<p>This workflow builds your Docker image and then scans it for vulnerabilities on every push to the repository.</p>
<h2 id="toc-4-limit-container-resources">4. Limit Container Resources</h2>
<p>Setting <a href="https://docs.docker.com/engine/containers/resource_constraints/">Docker resource limits</a> on your containers is crucial for preventing Denial of Service (DoS) attacks and ensuring fair resource allocation in multi-container environments.</p>
<p>In your <code>docker-compose.yml</code> file, you can set these limits as follows:</p>
<pre><code class="hljsgo">version: <span class="hljs-string">'3.8'</span>
services:
  web:
    image: nginx:<span class="hljs-number">1.21</span><span class="hljs-number">.3</span>
    deploy:
      resources:
        limits:
          cpus: <span class="hljs-string">'0.50'</span>
          memory: <span class="hljs-number">512</span>M
        reservations:
          cpus: <span class="hljs-string">'0.25'</span>
          memory: <span class="hljs-number">256</span>M
</code></pre>
<p>This configuration limits the <code>web</code> service to use a maximum of 0.5 CPU cores and 512MB of memory, while reserving a minimum of 0.25 CPU cores and 256MB of memory.</p>
<p>You can also set these limits when running a container using the Docker CLI:</p>
<pre><code class="hljsperl">docker run -d --name myapp --cpus=<span class="hljs-number">0</span>.<span class="hljs-number">5</span> --memory=<span class="hljs-number">512</span><span class="hljs-keyword">m</span> nginx:<span class="hljs-number">1.21</span>.<span class="hljs-number">3</span>
</code></pre>
<p>By setting these limits, you prevent a single container from consuming all available resources on the host, which could affect other containers or the host system itself.</p>
<h2 id="toc-5-use-non-root-users">5. Use Non-Root Users</h2>
<p>Running containers as root is a significant security risk. If an attacker manages to break out of the container, they would have root access to the host system. To mitigate this risk, create a non-root user in your Dockerfile and switch to this user.</p>
<p>Here's an example Dockerfile that creates a non-root user:</p>
<pre><code class="hljsperl">FROM node:<span class="hljs-number">18</span>
<span class="hljs-comment"># Create app directory</span>
WORKDIR /usr/src/app

<span class="hljs-comment"># Install app dependencies</span>
COPY <span class="hljs-keyword">package</span>*.json ./
RUN npm ci --only=production

<span class="hljs-comment"># Bundle app source</span>
COPY . .

<span class="hljs-comment"># Create a non-root user and switch to it</span>
RUN groupadd -r myapp &amp;&amp; useradd -r -g myapp myuser
USER myuser

EXPOSE <span class="hljs-number">8080</span>
CMD [ <span class="hljs-string">"node"</span>, <span class="hljs-string">"server.js"</span> ]
</code></pre>
<p>In this Dockerfile, we create a new user <code>myuser</code> and a group <code>myapp</code>, then switch to this user before running the application. This ensures that the application runs with limited permissions.</p>
<p>When running the container, you can also use the <code>--user</code> flag to specify a non-root user:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">docker</span> <span class="hljs-selector-tag">run</span> <span class="hljs-selector-tag">-d</span> <span class="hljs-selector-tag">--name</span> <span class="hljs-selector-tag">myapp</span> <span class="hljs-selector-tag">--user</span> 1000<span class="hljs-selector-pseudo">:1000</span> <span class="hljs-selector-tag">myimage</span>
</code></pre>
<p>This runs the container as the user with UID 1000 and GID 1000.</p>
<h2 id="toc-6-implement-secret-management">6. Implement Secret Management</h2>
<p>Storing sensitive information like passwords, API keys, and other secrets directly in your Dockerfile or Docker Compose file is a security risk. Instead, use <a href="https://docs.docker.com/engine/swarm/secrets/">Docker secrets</a> for managing this sensitive data.</p>
<p>First, create a secret:</p>
<pre><code class="hljsphp"><span class="hljs-keyword">echo</span> <span class="hljs-string">"mysecretpassword"</span> | docker secret create db_password -
</code></pre>
<p>Then, in your <code>docker-compose.yml</code> file, you can use this secret:</p>
<pre><code class="hljsjavascript">version: <span class="hljs-string">'3.8'</span>
<span class="hljs-attr">services</span>:
  db:
    image: mysql:<span class="hljs-number">8.0</span>
    <span class="hljs-attr">secrets</span>:
      - db_password
    <span class="hljs-attr">environment</span>:
      MYSQL_ROOT_PASSWORD_FILE: <span class="hljs-regexp">/run/</span>secrets/db_password
<span class="hljs-attr">secrets</span>:
  db_password:
    external: <span class="hljs-literal">true</span>
</code></pre>
<p>In this example, MySQL will read the root password from the file <code>/run/secrets/db_password</code> inside the container, which contains the secret we created.</p>
<p>For non-swarm mode, you can use environment variables and a <code>.env</code> file:</p>
<pre><code class="hljspowershell">version: <span class="hljs-string">'3.8'</span>
services:
  db:
    image: mysql:<span class="hljs-number">8.0</span>
    environment:
      MYSQL_ROOT_PASSWORD: <span class="hljs-variable">$</span>{DB_PASSWORD}
</code></pre>
<p>And in your <code>.env</code> file (which should be added to <code>.gitignore</code>):</p>
<pre><code class="hljs">DB_PASSWORD=mysecretpassword
</code></pre>
<p>This approach keeps secrets out of your version-controlled files while still allowing easy configuration.</p>
<h2 id="toc-7-enable-content-trust">7. Enable Content Trust</h2>
<p><a href="https://docs.docker.com/engine/security/trust/">Docker Content Trust</a> (DCT) allows you to verify the integrity and the publisher of all the data received from a registry over any channel. When DCT is enabled, you can be sure that the image you're pulling is the one that was pushed, without any tampering.</p>
<p>To enable DCT, set the <code>DOCKER_CONTENT_TRUST</code> environment variable:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">export</span> DOCKER_CONTENT_TRUST=<span class="hljs-number">1</span>
</code></pre>
<p>With DCT enabled, when you push an image, Docker signs it:</p>
<pre><code class="hljsperl">docker <span class="hljs-keyword">push</span> myrepo/myimage:latest
</code></pre>
<p>When pulling images with DCT enabled, Docker will only pull signed images:</p>
<pre><code class="hljs">docker pull myrepo/myimage:latest
</code></pre>
<p>If the image isn't signed or the signature doesn't match, the pull will fail.</p>
<p>You can also enable DCT in your Docker daemon configuration file (<code>/etc/docker/daemon.json</code>):</p>
<pre><code class="hljsgo">{
  <span class="hljs-string">"content-trust"</span>: {
    <span class="hljs-string">"mode"</span>: <span class="hljs-string">"enforced"</span>
  }
}
</code></pre>
<p>This enforces DCT for all operations on the Docker daemon.</p>
<h2 id="toc-8-use-read-only-containers">8. Use Read-Only Containers</h2>
<p><a href="https://docs.docker.com/reference/compose-file/services/#read_only">Running containers in read-only mode</a> prevents attackers from making changes to the container's filesystem, even if they manage to execute arbitrary code within the container.</p>
<p>In your <code>docker-compose.yml</code> file, you can set a container to read-only mode like this:</p>
<pre><code class="hljsgo">version: <span class="hljs-string">'3.8'</span>
services:
  web:
    image: nginx:<span class="hljs-number">1.21</span><span class="hljs-number">.3</span>
    read_only: <span class="hljs-literal">true</span>
    tmpfs:
      - /tmp
      - /<span class="hljs-keyword">var</span>/cache/nginx
</code></pre>
<p>The <code>tmpfs</code> mounts are necessary for Nginx to function correctly, as it needs to write to these directories. These are temporary filesystems that exist only in memory.</p>
<p>When using the Docker CLI, you can use the <code>--read-only</code> flag:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">docker</span> <span class="hljs-selector-tag">run</span> <span class="hljs-selector-tag">-d</span> <span class="hljs-selector-tag">--name</span> <span class="hljs-selector-tag">myapp</span> <span class="hljs-selector-tag">--read-only</span> <span class="hljs-selector-tag">nginx</span><span class="hljs-selector-pseudo">:1.21.3</span>
</code></pre>
<p>For applications that need to write data, you can use volumes or bind mounts for specific directories that need write access, while keeping the rest of the filesystem read-only.</p>
<h2 id="toc-9-implement-network-segmentation">9. Implement Network Segmentation</h2>
<p><a href="https://docs.docker.com/reference/compose-file/networks/">Network segmentation in Docker</a> helps to isolate containers and reduce the attack surface. By default, all containers on a Docker network can communicate with each other. However, you can create separate networks for different groups of containers.</p>
<p>Here's an example <code>docker-compose.yml</code> file that implements network segmentation:</p>
<pre><code class="hljsmarkdown">version: '3.8'
services:
  frontend:
<span class="hljs-code">    image: nginx:1.21.3</span>
<span class="hljs-code">    networks:</span>
<span class="hljs-bullet">      - </span>frontend
  backend:
<span class="hljs-code">    image: app:latest</span>
<span class="hljs-code">    networks:</span>
<span class="hljs-bullet">      - </span>backend
  db:
<span class="hljs-code">    image: mysql:8.0</span>
<span class="hljs-code">    networks:</span>
<span class="hljs-bullet">      - </span>backend
networks:
  frontend:
<span class="hljs-code">    driver: bridge</span>
  backend:
<span class="hljs-code">    driver: bridge</span>
</code></pre>
<p>In this setup, the <code>frontend</code> service cannot communicate directly with the <code>db</code> service, as they're on different networks. The <code>backend</code> service acts as a bridge between the two.</p>
<p>You can also create and manage networks using the Docker CLI:</p>
<pre><code class="hljscss"># <span class="hljs-selector-tag">Create</span> <span class="hljs-selector-tag">networks</span>
<span class="hljs-selector-tag">docker</span> <span class="hljs-selector-tag">network</span> <span class="hljs-selector-tag">create</span> <span class="hljs-selector-tag">frontend</span>
<span class="hljs-selector-tag">docker</span> <span class="hljs-selector-tag">network</span> <span class="hljs-selector-tag">create</span> <span class="hljs-selector-tag">backend</span>

# <span class="hljs-selector-tag">Run</span> <span class="hljs-selector-tag">containers</span> <span class="hljs-selector-tag">on</span> <span class="hljs-selector-tag">specific</span> <span class="hljs-selector-tag">networks</span>
<span class="hljs-selector-tag">docker</span> <span class="hljs-selector-tag">run</span> <span class="hljs-selector-tag">-d</span> <span class="hljs-selector-tag">--name</span> <span class="hljs-selector-tag">nginx</span> <span class="hljs-selector-tag">--network</span> <span class="hljs-selector-tag">frontend</span> <span class="hljs-selector-tag">nginx</span><span class="hljs-selector-pseudo">:1.21.3</span>
<span class="hljs-selector-tag">docker</span> <span class="hljs-selector-tag">run</span> <span class="hljs-selector-tag">-d</span> <span class="hljs-selector-tag">--name</span> <span class="hljs-selector-tag">app</span> <span class="hljs-selector-tag">--network</span> <span class="hljs-selector-tag">frontend</span> <span class="hljs-selector-tag">--network</span> <span class="hljs-selector-tag">backend</span> <span class="hljs-selector-tag">app</span><span class="hljs-selector-pseudo">:latest</span>
<span class="hljs-selector-tag">docker</span> <span class="hljs-selector-tag">run</span> <span class="hljs-selector-tag">-d</span> <span class="hljs-selector-tag">--name</span> <span class="hljs-selector-tag">mysql</span> <span class="hljs-selector-tag">--network</span> <span class="hljs-selector-tag">backend</span> <span class="hljs-selector-tag">mysql</span><span class="hljs-selector-pseudo">:8.0</span>
</code></pre>
<p>This network segmentation adds an extra layer of security by limiting the potential spread of an attack if one container is compromised.</p>
<h2 id="toc-10-regular-security-audits">10. Regular Security Audits</h2>
<p>Regular security audits are crucial for maintaining the security of your Docker environment. <a href="https://github.com/docker/docker-bench-security">Docker Bench for Security</a> is a script that checks for dozens of common best practices around deploying Docker containers in production.</p>
<p>To run Docker Bench for Security:</p>
<pre><code class="hljsjavascript">docker run -it --net host --pid host --userns host --cap-add audit_control \
    -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
    -v /<span class="hljs-keyword">var</span>/lib:<span class="hljs-regexp">/var/</span>lib \
    -v /<span class="hljs-keyword">var</span>/run/docker.sock:<span class="hljs-regexp">/var/</span>run/docker.sock \
    -v /usr/lib/systemd:<span class="hljs-regexp">/usr/</span>lib/systemd \
    -v /etc:<span class="hljs-regexp">/etc --label docker_bench_security \
    docker/</span>docker-bench-security
</code></pre>
<p>This command runs a series of checks and provides a report with recommendations for improving your Docker security posture.</p>
<p>It's a good practice to run this audit regularly, such as weekly or after any significant changes to your Docker environment. You can automate this process by integrating it into your CI/CD pipeline or setting up a cron job.</p>
<p>Here's an example of how you might integrate Docker Bench into a GitHub Actions workflow:</p>
<pre><code class="hljsphp">name: Docker Security Audit

on:
  schedule:
    - cron: <span class="hljs-string">'0 0 * * 0'</span>  <span class="hljs-comment"># Run weekly on Sunday at midnight</span>

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
    - name: Check out code
      uses: actions/checkout@v2
    - name: Run Docker Bench <span class="hljs-keyword">for</span> Security
      run: |
        docker run --net host --pid host --userns host --cap-add audit_control \
          -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
          -v /<span class="hljs-keyword">var</span>/lib:/<span class="hljs-keyword">var</span>/lib \
          -v /<span class="hljs-keyword">var</span>/run/docker.sock:/<span class="hljs-keyword">var</span>/run/docker.sock \
          -v /usr/lib/systemd:/usr/lib/systemd \
          -v /etc:/etc --label docker_bench_security \
          docker/docker-bench-security
</code></pre>
<p>This workflow runs Docker Bench for Security every week and provides a report in the GitHub Actions log.</p>
<h2 id="toc-11-implement-logging-and-monitoring">11. Implement Logging and Monitoring</h2>
<p>Proper logging and monitoring are essential for detecting and responding to security incidents in your Docker environment. Docker provides built-in logging mechanisms that you can configure for each container:</p>
<blockquote>
<p><a href="https://docs.docker.com/engine/logging/configure/">Docker Logging Drivers</a></p>
</blockquote>
<p>In your <code>docker-compose.yml</code> file, you can set up logging like this:</p>
<pre><code class="hljspowershell">version: <span class="hljs-string">'3.8'</span>
services:
  web:
    image: nginx:<span class="hljs-number">1.21</span>.<span class="hljs-number">3</span>
    logging:
      driver: <span class="hljs-string">"json-file"</span>
      options:
        max<span class="hljs-literal">-size</span>: <span class="hljs-string">"200m"</span>
        max<span class="hljs-operator">-file</span>: <span class="hljs-string">"10"</span>
</code></pre>
<p>This configuration uses the <code>json-file</code> logging driver, which is the default. It sets a maximum size of 200MB for each log file and keeps up to 10 log files.</p>
<p>For more advanced logging, you can use the <code>syslog</code> driver to send logs to a central syslog server:</p>
<pre><code class="hljsgo">logging:
  driver: syslog
  options:
    syslog-address: <span class="hljs-string">"udp://192.168.0.42:1234"</span>
</code></pre>
<p>For monitoring, consider using tools like Prometheus and Grafana. Here's an example <code>docker-compose.yml</code> that sets up a basic monitoring stack:</p>
<pre><code class="hljsmarkdown">version: '3.8'
services:
  prometheus:
<span class="hljs-code">    image: prom/prometheus</span>
<span class="hljs-code">    volumes:</span>
<span class="hljs-bullet">      - </span>./prometheus.yml:/etc/prometheus/prometheus.yml
<span class="hljs-code">    ports:</span>
<span class="hljs-bullet">      - </span>"9090:9090"
  grafana:
<span class="hljs-code">    image: grafana/grafana</span>
<span class="hljs-code">    depends_on:</span>
<span class="hljs-bullet">      - </span>prometheus
<span class="hljs-code">    ports:</span>
<span class="hljs-bullet">      - </span>"3000:3000"
</code></pre>
<p>You'll need to configure Prometheus to scrape metrics from your Docker daemon and containers. Here's a basic <code>prometheus.yml</code> configuration:</p>
<pre><code class="hljspython"><span class="hljs-keyword">global</span>:
  scrape_interval: <span class="hljs-number">15</span>s

scrape_configs:
  - job_name: <span class="hljs-string">'docker'</span>
    static_configs:
      - targets: [<span class="hljs-string">'docker.for.mac.localhost:9323'</span>]
</code></pre>
<p>To enable Docker metrics, you need to configure your Docker daemon. Add the following to <code>/etc/docker/daemon.json</code>:</p>
<pre><code class="hljsgo">{
  <span class="hljs-string">"metrics-addr"</span> : <span class="hljs-string">"0.0.0.0:9323"</span>,
  <span class="hljs-string">"experimental"</span> : <span class="hljs-literal">true</span>
}
</code></pre>
<p>Remember to restart the Docker daemon after making these changes.</p>
<p>With this setup, you can create dashboards in Grafana to visualize your Docker metrics and set up alerts for any suspicious activity.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Security is an ongoing process that requires regular attention and updates!</p>
<p>These 11 security tips will significantly help you with your container security and Docker environment but you should always keep an eye on the latest security best practices and updates.</p>
<p>For more information on Docker, check out my <a href="https://devdojo.com/bobbyiliev/free-introduction-to-docker-ebook">free Introduction to Docker eBook</a>.</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>If you're looking for a reliable platform to practice these security tips, consider using DigitalOcean. They offer a <a href="https://m.do.co/c/2a9bba940f39">$200 free credit</a> to get you started.</p>
]]></description>
                                                            <category>docker</category>
                                            <category>linux</category>
                                            <category>devops</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12415</guid>
                <pubDate>Sat, 05 Oct 2024 06:58:14 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Optimizing Docker Image Sizes: Advanced Techniques and Tools]]></title>
                <link>https://devdojo.com/bobbyiliev/optimizing-docker-image-sizes-advanced-techniques-and-tools</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>I'm Bobby, a Docker Captain and the author of the <a href="https://devdojo.com/bobbyiliev/free-introduction-to-docker-ebook">free Introduction to Docker eBook</a>. Today, we're diving deep into a crucial aspect of Docker that can significantly impact your containerized applications' performance and resource utilization: optimizing Docker image sizes.</p>
<p>The efficiency of our Docker images is increasingly important, especially in cloud-native and microservices architectures where we might be deploying hundreds or thousands of containers. Every megabyte counts when you're scaling up!</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>Before we embark on this image-slimming journey, make sure you have:</p>
<ul>
<li>Docker installed on your system</li>
<li>Basic knowledge of Dockerfiles and Docker commands</li>
<li>Some experience building and running Docker containers</li>
</ul>
<p>If you're new to Docker or need a refresher, I highly recommend checking out my <a href="https://devdojo.com/bobbyiliev/free-introduction-to-docker-ebook">free Introduction to Docker eBook</a>. It covers all the fundamentals you'll need to follow along with this guide and will give you a solid foundation for understanding the optimization techniques we'll discuss.</p>
<p></p><div style="width:100%;height:0;padding-bottom:56%;position:relative;display:block"><iframe src="https://giphy.com/embed/3o7TKUM3IgJBX2as9O" width="100%" height="100%" style="position:absolute" frameborder="0" class="giphy-embed" allowfullscreen></iframe></div>
<h2 id="why-optimize-docker-image-sizes">Why Optimize Docker Image Sizes?</h2>
<p>Before we dive into the specific aproaches, let's take a moment to understand why smaller Docker images are so beneficial:</p>
<ol>
<li>
<p>Smaller images mean less data to transfer. This translates to quicker pulls from registries, faster deployments, and reduced time to scale up your applications.</p>
</li>
<li>
<p>In cloud environments, storage isn't free. Smaller images consume less storage space, which can lead to significant cost savings, especially when you're dealing with multiple images and frequent updates.</p>
</li>
<li>
<p>A smaller image means a reduced attack surface. With fewer packages and files, there's less opportunity for vulnerabilities to creep in. It's easier to audit and maintain the security of a lean image.</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
</li>
<li>
<p>Smaller images often lead to faster container startup times. In orchestrated environments like Kubernetes, where containers might be frequently started and stopped, this can make a big difference in overall system responsiveness.</p>
</li>
<li>
<p>In environments with limited resources, such as edge computing or IoT devices, every byte counts. Smaller images allow you to run more containers on the same hardware.</p>
</li>
</ol>
<p>Now that we understand the importance of optimizing our Docker images, let's dive into the techniques that can help us achieve this goal.</p>
<h2 id="toc-1-use-minimal-base-images">1. Use Minimal Base Images</h2>
<p>One of the most effective ways to reduce your Docker image size is to start with a minimal base image. The base image is the foundation of your Docker image, and choosing the right one can make a significant difference.</p>
<p>Instead of using a full-fledged operating system image like:</p>
<pre><code class="hljs">FROM ubuntu
</code></pre>
<p>Consider using a more lightweight alternative:</p>
<pre><code class="hljs">FROM alpine
</code></pre>
<p>Alpine Linux is a security-oriented, lightweight Linux distribution that's only 5MB in size. It's perfect for creating small, secure Docker images. However, it uses musl libc instead of glibc, which can occasionally cause compatibility issues with some applications.</p>
<p>For even more minimalism, especially for compiled languages, consider using distroless images:</p>
<pre><code class="hljspowershell">FROM gcr.io/distroless/<span class="hljs-keyword">static</span><span class="hljs-literal">-debian11</span>
</code></pre>
<p>Distroless images contain only your application and its runtime dependencies. They don't contain package managers, shells, or any other programs you would expect to find in a standard Linux distribution. This makes them extremely small and secure.</p>
<p>Here's a comparison of base image sizes:</p>
<ul>
<li>Ubuntu: ~72MB</li>
<li>Alpine: ~5MB</li>
<li>Distroless: ~2MB</li>
</ul>
<p>To learn more about distroless images, check out this blog post: <a href="https://www.docker.com/blog/is-your-container-image-really-distroless/">Is Your Container Image Really Distroless?</a>.</p>
<p>By choosing the right base image, you can reduce your starting point from hundreds of MBs to just a few MBs. This sets the stage for a much smaller final image.</p>
<h2 id="toc-2-multi-stage-builds-the-secret-sauce">2. Multi-stage Builds: The Secret Sauce</h2>
<p>Multi-stage builds are a game-changer for creating efficient Docker images. They allow you to use one image for building your application and another for running it. This technique is particularly powerful for compiled languages but can be useful for interpreted languages as well.</p>
<p>Here's an expanded example for a Go application:</p>
<pre><code class="hljspython"><span class="hljs-comment"># Build stage</span>
FROM golang:<span class="hljs-number">1.21</span>-alpine AS builder
WORKDIR /app
COPY . .
<span class="hljs-comment"># Install any necessary dependencies</span>
RUN go mod download
<span class="hljs-comment"># Build the application</span>
RUN CGO_ENABLED=<span class="hljs-number">0</span> GOOS=linux go build -a -installsuffix cgo -o main .

<span class="hljs-comment"># Final stage</span>
FROM alpine:<span class="hljs-number">3.18</span>
RUN apk --no-cache add ca-certificates
WORKDIR /root/
<span class="hljs-comment"># Copy the pre-built binary file from the previous stage</span>
COPY --<span class="hljs-keyword">from</span>=builder /app/main .
<span class="hljs-comment"># Command to run the executable</span>
CMD [<span class="hljs-string">"./main"</span>]
</code></pre>
<p>Let's break this down:</p>
<ol>
<li>
<p>The first stage uses the <code>golang:1.21-alpine</code> image, which includes the Go compiler and tools. We build our application in this stage.</p>
</li>
<li>
<p>We use <code>CGO_ENABLED=0</code> and <code>GOOS=linux</code> to create a statically linked binary that doesn't depend on C libraries. This allows us to use a very minimal runtime image.</p>
</li>
<li>
<p>The second stage starts from the Alpine image, which is much smaller than the Go image.</p>
</li>
<li>
<p>We only copy the compiled binary from the build stage to the final stage. All the Go development tools and source code are left behind.</p>
</li>
<li>
<p>We add ca-certificates to ensure our application can make HTTPS connections if needed.</p>
</li>
</ol>
<p>This approach separates your build environment from your runtime environment, resulting in a much smaller final image. It's not uncommon to see image sizes reduce from 300-400MB to 10-20MB using this technique.</p>
<p>Multi-stage builds can be adapted for other languages too. For example, in a Node.js application, you might use one stage to install all dependencies and build your application, and another stage with only production dependencies for the final image. The same principle applies for Python, Java, and other languages.</p>
<h2 id="toc-3-layer-optimization-every-line-counts">3. Layer Optimization: Every Line Counts</h2>
<p>In Docker, each instruction in your Dockerfile creates a new layer. These layers are cached, which can speed up builds, but they also contribute to the final image size. To minimize layers and optimize your image:</p>
<ol>
<li>
<p><strong>Combine RUN commands</strong>: Use <code>&amp;&amp;</code> to chain commands and <code>\</code> for line breaks to make your Dockerfile more readable. This reduces the number of layers and can significantly decrease your image size.</p>
<p>Instead of:</p>
<pre><code class="hljsjavascript">RUN apt-<span class="hljs-keyword">get</span> update
RUN apt-<span class="hljs-keyword">get</span> install -y package1
RUN apt-<span class="hljs-keyword">get</span> install -y package2
RUN apt-<span class="hljs-keyword">get</span> install -y package3
RUN rm -rf /var/lib/apt/lists/*
</code></pre>
<p>Use:</p>
<pre><code class="hljsjavascript">RUN apt-<span class="hljs-keyword">get</span> update &amp;&amp; apt-<span class="hljs-keyword">get</span> install -y \
    package1 \
    package2 \
    package3 \
 &amp;&amp; rm -rf /var/lib/apt/lists/*
</code></pre>
<p>This not only reduces the number of layers but also ensures that the package lists are updated and cleaned up in the same layer, preventing outdated package lists from persisting in your image.</p>
</li>
<li>
<p><strong>Use COPY instead of ADD</strong>: <code>COPY</code> is more transparent in its behavior. Use <code>ADD</code> only when you specifically need its tar auto-extraction feature or copying remote files from URLs. For more information check out the documentation <a href="https://docs.docker.com/build/building/best-practices/#add-or-copy">here</a>.</p>
</li>
<li>
<p><strong>Order instructions from least to most frequently changing</strong>: Docker caches layers, and this cache is invalidated when the content used in an instruction changes. By putting instructions that change frequently (like copying your application code) towards the end of your Dockerfile, you can take better advantage of the build cache for the layers that change less frequently.</p>
<p>For example:</p>
<pre><code class="hljsperl">FROM node:<span class="hljs-number">14</span>-alpine
WORKDIR /app

<span class="hljs-comment"># These layers change less frequently</span>
COPY <span class="hljs-keyword">package</span>*.json ./
RUN npm ci --only=production

<span class="hljs-comment"># This layer changes most frequently</span>
COPY . .

CMD [<span class="hljs-string">"node"</span>, <span class="hljs-string">"server.js"</span>]
</code></pre>
</li>
<li>
<p><strong>Use .dockerignore</strong>: This file works like .gitignore and prevents unnecessary files from being added to your build context. This can significantly speed up the build process and reduce the final image size.</p>
<p>Example .dockerignore:</p>
<pre><code class="hljscss"><span class="hljs-selector-class">.git</span>
*<span class="hljs-selector-class">.md</span>
<span class="hljs-selector-tag">node_modules</span>
<span class="hljs-selector-tag">npm-debug</span><span class="hljs-selector-class">.log</span>
</code></pre>
<p>This prevents the .git directory, Markdown files, the node_modules directory, and npm debug logs from being copied into your image.</p>
</li>
<li>
<p><strong>Clean up in the same layer</strong>: When installing packages or downloading files, make sure to clean up in the same RUN instruction:</p>
<pre><code class="hljspowershell">RUN wget https://example.com/big<span class="hljs-operator">-file</span>.tar.gz \
    &amp;&amp; tar <span class="hljs-literal">-xzf</span> big<span class="hljs-operator">-file</span>.tar.gz \
    &amp;&amp; make <span class="hljs-literal">-C</span> big<span class="hljs-operator">-file</span> \
    &amp;&amp; rm <span class="hljs-literal">-rf</span> big<span class="hljs-operator">-file</span> big<span class="hljs-operator">-file</span>.tar.gz
</code></pre>
<p>This ensures that the downloaded and extracted files don't persist in the final image.</p>
</li>
</ol>
<p>By carefully considering each instruction in your Dockerfile and how it affects layering, you can create more efficient, smaller images that are faster to build and deploy.</p>
<h2 id="toc-4-use-dockerignore">4. Use <code>.dockerignore</code></h2>
<p>The <code>.dockerignore</code> file is a powerful tool for optimizing your Docker builds and reducing image sizes. It works similarly to <code>.gitignore</code>, allowing you to specify which files and directories should be excluded from the Docker build context.</p>
<p>Here's why <code>.dockerignore</code> is crucial:</p>
<ol>
<li>
<p>The build context is sent to the Docker daemon before the build starts. A smaller context means faster uploads and builds.</p>
</li>
<li>
<p>By excluding files that change frequently but aren't needed in the image, you can prevent unnecessary cache invalidation and speed up builds.</p>
</li>
<li>
<p>It helps prevent sensitive files (like .env files or ssh keys) from accidentally being included in your image.</p>
</li>
</ol>
<p>Here's an example of a comprehensive <code>.dockerignore</code> file:</p>
<pre><code class="hljsmarkdown"><span class="hljs-section"># Version control</span>
.git
.gitignore

<span class="hljs-section"># Documentation</span>
*.md
docs/

<span class="hljs-section"># Development artifacts</span>
node_modules/
npm-debug.log
yarn-error.log
*.log
*.bak

<span class="hljs-section"># Build output</span>
dist/
build/
*.exe
*.dll
*.so
*.dylib

<span class="hljs-section"># Editor and IDE files</span>
.vscode/
.idea/
*.swp
*.swo

<span class="hljs-section"># OS generated files</span>
.DS_Store
Thumbs.db

<span class="hljs-section"># Test files</span>
test/
<span class="hljs-strong">__tests__</span>/
*.test.js

<span class="hljs-section"># Configuration files that shouldn't be in the image</span>
.env
.env.*
config.local.js

<span class="hljs-section"># Docker files</span>
Dockerfile
docker-compose.yml
</code></pre>
<p>By carefully crafting your <code>.dockerignore</code> file, you can significantly reduce your build context size and ensure that only the necessary files are included in your Docker image. This not only speeds up your build process but also helps in creating smaller, more focused images.</p>
<p>Remember to review and update your <code>.dockerignore</code> file regularly, especially as your project structure changes or grows.</p>
<h2 id="toc-5-use-build-arguments-for-flexibility">5. Use Build Arguments for Flexibility</h2>
<p>Build arguments provide a powerful way to create flexible and reusable Dockerfiles. They allow you to pass variables at build-time, enabling you to customize your image building process without maintaining multiple Dockerfiles.</p>
<p>Here's an in-depth look at how to use build arguments effectively:</p>
<ol>
<li>
<p><strong>Defining build arguments</strong>:
In your Dockerfile, you can define build arguments using the <code>ARG</code> instruction:</p>
<pre><code class="hljspowershell">ARG VERSION=latest
FROM base:<span class="hljs-variable">$</span>{VERSION}
</code></pre>
<p>This sets a default value of "latest" for the VERSION argument, which can be overridden at build time.</p>
</li>
<li>
<p><strong>Using build arguments</strong>:
You can use build arguments in various ways throughout your Dockerfile:</p>
<pre><code class="hljspowershell">ARG NODE_ENV=production
ENV NODE_ENV=<span class="hljs-variable">$</span>{NODE_ENV}

ARG USER=nobody
USER <span class="hljs-variable">$</span>{USER}

ARG PORT=<span class="hljs-number">8080</span>
EXPOSE <span class="hljs-variable">$</span>{PORT}
</code></pre>
</li>
<li>
<p><strong>Passing build arguments</strong>:
When building your image, you can pass values for your build arguments:</p>
<pre><code class="hljs">docker build --build-arg VERSION=18.04 --build-arg NODE_ENV=development -t myapp:custom .
</code></pre>
</li>
<li>
<p><strong>Multi-stage builds with arguments</strong>:
Build arguments can be particularly powerful in multi-stage builds:</p>
<pre><code class="hljspowershell">ARG GO_VERSION=<span class="hljs-number">1.21</span>
FROM golang:<span class="hljs-variable">$</span>{GO_VERSION}<span class="hljs-literal">-alpine</span> AS builder
<span class="hljs-comment"># ... build stage instructions ...</span>

ARG ALPINE_VERSION=<span class="hljs-number">3.18</span>
FROM alpine:<span class="hljs-variable">$</span>{ALPINE_VERSION}
<span class="hljs-comment"># ... final stage instructions ...</span>
</code></pre>
<p>This allows you to easily update the versions of your base images by passing different build arguments.</p>
</li>
<li>
<p><strong>Scoping build arguments</strong>:
It's important to note that each <code>FROM</code> instruction in a multi-stage Dockerfile clears all ARGs defined before it. If you need an ARG in multiple stages, you need to redefine it:</p>
<pre><code class="hljspowershell">ARG VERSION=latest
FROM base:<span class="hljs-variable">$</span>{VERSION} AS builder
ARG VERSION  <span class="hljs-comment"># Redefine to use in this stage</span>
RUN echo <span class="hljs-string">"Building version: <span class="hljs-variable">$</span>{VERSION}"</span>

FROM base:<span class="hljs-variable">$</span>{VERSION} AS final
ARG VERSION  <span class="hljs-comment"># Redefine again for this stage</span>
LABEL version=<span class="hljs-string">"<span class="hljs-variable">$</span>{VERSION}"</span>
</code></pre>
</li>
<li>
<p><strong>Security considerations</strong>:
Be cautious with sensitive information in build arguments. While they're not persisted in the final image like ENV variables, they are visible in the image history. For sensitive data, consider using Docker secrets or environment variables at runtime instead.</p>
</li>
</ol>
<p>By using build arguments, you can create more flexible and maintainable Dockerfiles. This approach allows you to use the same Dockerfile for different environments (development, staging, production) or to easily update versions of base images and dependencies without modifying the Dockerfile itself.</p>
<h2 id="toc-6-use-docker-s-buildkit">6. Use Docker's BuildKit</h2>
<p>BuildKit is Docker's next-generation build engine, offering improved performance, better caching, and more advanced features. It's designed to be faster and more efficient than the legacy builder, especially for complex build scenarios.</p>
<p>Here's a deep dive into using BuildKit for optimizing your Docker builds:</p>
<ol>
<li>
<p><strong>Enabling BuildKit</strong>:
You can enable BuildKit by setting an environment variable:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">export</span> DOCKER_BUILDKIT=<span class="hljs-number">1</span>
</code></pre>
<p>Or, for a single build:</p>
<pre><code class="hljs">DOCKER_BUILDKIT=1 docker build .
</code></pre>
<p>You can also enable it by default in the Docker daemon configuration file (<code>/etc/docker/daemon.json</code>):</p>
<pre><code class="hljsgo">{
  <span class="hljs-string">"features"</span>: {
    <span class="hljs-string">"buildkit"</span>: <span class="hljs-literal">true</span>
  }
}
</code></pre>
</li>
<li>
<p><strong>Syntax directive</strong>:
To use BuildKit-specific features, add this line at the top of your Dockerfile:</p>
<pre><code class="hljsmarkdown"><span class="hljs-section"># syntax=docker/dockerfile:1.4</span>
</code></pre>
<p>This enables the use of the latest Dockerfile syntax and BuildKit features.</p>
</li>
<li>
<p><strong>Improved caching with <code>--mount=type=cache</code></strong>:
BuildKit introduces a new caching mechanism that can significantly speed up builds:</p>
<pre><code class="hljsgo">RUN --mount=<span class="hljs-keyword">type</span>=cache,target=/root/.cache/<span class="hljs-keyword">go</span>-build \
    <span class="hljs-keyword">go</span> build -o myapp .
</code></pre>
<p>This caches the Go build cache between builds, greatly speeding up subsequent builds.</p>
</li>
<li>
<p><strong>Secret mounting</strong>:
BuildKit allows you to securely use secrets during build without them being stored in the final image:</p>
<pre><code class="hljsgo">RUN --mount=<span class="hljs-keyword">type</span>=secret,id=mysecret cat /run/secrets/mysecret
</code></pre>
<p>You can then pass the secret at build time:</p>
<pre><code class="hljs">docker build --secret id=mysecret,src=path/to/secret.txt .
</code></pre>
</li>
<li>
<p><strong>SSH agent forwarding</strong>:
BuildKit can forward your SSH agent, allowing secure cloning of private repositories during build:</p>
<pre><code class="hljsgo">RUN --mount=<span class="hljs-keyword">type</span>=ssh ssh-add -l &amp;&amp; \
    git clone git@github.com:myorg/myrepo.git
</code></pre>
<p>Build with:</p>
<pre><code class="hljsgo">docker build --ssh <span class="hljs-keyword">default</span> .
</code></pre>
</li>
<li>
<p><strong>Parallel execution</strong>:
BuildKit can execute independent build stages in parallel, potentially speeding up complex builds.</p>
</li>
<li>
<p><strong>Better output formatting</strong>:
BuildKit provides cleaner, more informative build output, making it easier to understand what's happening during the build process.</p>
</li>
<li>
<p><strong>Inline cache storage</strong>:
BuildKit can store cache metadata in the image itself, allowing you to push and pull cached layers along with your image:</p>
<pre><code class="hljsgo">docker build --cache-from myimage:latest --cache-to <span class="hljs-keyword">type</span>=inline .
</code></pre>
</li>
<li>
<p><strong>Multi-platform builds</strong>:
BuildKit simplifies creating multi-architecture images:</p>
<pre><code class="hljs">docker buildx build --platform linux/amd64,linux/arm64 -t myimage:latest .
</code></pre>
</li>
</ol>
<p>BuildKit is the future of Docker builds and offers many advanced features that can help optimize your build process. You can check out the <a href="https://docs.docker.com/build/buildkit/">official BuildKit documentation</a> for more details.</p>
<h2 id="toc-7-optimize-for-your-specific-language">7. Optimize for Your Specific Language</h2>
<p>Different programming languages have different optimization techniques when it comes to Docker images. Here's an in-depth look at optimizing for some popular languages:</p>
<h3 id="node-js">Node.js</h3>
<ol>
<li>
<p><strong>Use <code>npm ci</code> instead of <code>npm install</code></strong>:</p>
<pre><code class="hljsgo">COPY <span class="hljs-keyword">package</span>*.json ./
RUN npm ci --only=production
</code></pre>
<p><code>npm ci</code> is faster and more reliable for CI environments. It installs exact versions from package-lock.json.</p>
</li>
<li>
<p><strong>Prune development dependencies</strong>:</p>
<pre><code class="hljs">RUN npm prune --production
</code></pre>
<p>This removes dev dependencies after your build step, significantly reducing image size.</p>
</li>
<li>
<p><strong>Use the official Node.js Alpine image</strong>:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">FROM</span> <span class="hljs-selector-tag">node</span><span class="hljs-selector-pseudo">:14-alpine</span>
</code></pre>
<p>Alpine-based images are much smaller than the default Node.js images.</p>
</li>
<li>
<p><strong>Use multi-stage builds for front-end applications</strong>:</p>
<pre><code class="hljsgo">FROM node:<span class="hljs-number">14</span>-alpine AS builder
WORKDIR /app
COPY <span class="hljs-keyword">package</span>*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
</code></pre>
<p>This builds your application in one stage and copies only the built assets to the final nginx image.</p>
</li>
</ol>
<h3 id="python">Python</h3>
<ol>
<li>
<p><strong>Use <code>pip install --no-cache-dir</code></strong>:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">RUN</span> <span class="hljs-selector-tag">pip</span> <span class="hljs-selector-tag">install</span> <span class="hljs-selector-tag">--no-cache-dir</span> <span class="hljs-selector-tag">-r</span> <span class="hljs-selector-tag">requirements</span><span class="hljs-selector-class">.txt</span>
</code></pre>
<p>This prevents pip from caching downloaded packages, reducing the image size.</p>
</li>
<li>
<p><strong>Use virtual environments</strong>:</p>
<pre><code class="hljsperl">RUN python -<span class="hljs-keyword">m</span> venv /opt/venv
ENV PATH=<span class="hljs-string">"/opt/venv/bin:$PATH"</span>
RUN pip install --<span class="hljs-keyword">no</span>-cache-dir -r requirements.txt
</code></pre>
<p>This keeps your Python environment isolated and can help reduce size.</p>
</li>
<li>
<p><strong>Use Python Alpine images</strong>:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">FROM</span> <span class="hljs-selector-tag">python</span><span class="hljs-selector-pseudo">:3.9-alpine</span>
</code></pre>
<p>Alpine-based Python images are much smaller than the standard ones.</p>
</li>
<li>
<p><strong>Compile Python bytecode</strong>:</p>
<pre><code class="hljsperl">RUN python -<span class="hljs-keyword">m</span> compileall .
</code></pre>
<p>This can slightly reduce size and improve startup time.</p>
</li>
</ol>
<h3 id="java">Java</h3>
<ol>
<li>
<p><strong>Use JDK for build, JRE for runtime</strong>:</p>
<pre><code class="hljsruby">FROM <span class="hljs-symbol">openjdk:</span><span class="hljs-number">11</span> AS builder
COPY . /app
RUN javac /app/Main.java

FROM <span class="hljs-symbol">openjdk:</span><span class="hljs-number">11</span>-jre
COPY --from=builder /app/Main<span class="hljs-class">.<span class="hljs-keyword">class</span> /<span class="hljs-title">app</span>/</span>
CMD [<span class="hljs-string">"java"</span>, <span class="hljs-string">"-cp"</span>, <span class="hljs-string">"/app"</span>, <span class="hljs-string">"Main"</span>]
</code></pre>
<p>This uses the full JDK to compile, but only the JRE to run.</p>
</li>
<li>
<p><strong>Use jlink to create a custom JRE</strong>:</p>
<pre><code class="hljsjavascript">FROM openjdk:<span class="hljs-number">11</span> AS builder
WORKDIR /app
COPY . .
RUN jlink --add-modules java.base,java.logging --output /javaruntime

FROM alpine:<span class="hljs-number">3.18</span>
COPY --<span class="hljs-keyword">from</span>=builder /javaruntime /opt/java
COPY --<span class="hljs-keyword">from</span>=builder /app/Main.class /app/
ENV PATH=<span class="hljs-string">"/opt/java/bin:${PATH}"</span>
CMD [<span class="hljs-string">"java"</span>, <span class="hljs-string">"-cp"</span>, <span class="hljs-string">"/app"</span>, <span class="hljs-string">"Main"</span>]
</code></pre>
<p>This creates a minimal JRE with only the modules your application needs.</p>
</li>
<li>
<p><strong>Use Spring Boot's layered jars</strong>:
For Spring Boot applications, use the layered jar feature to create more efficient Docker images:</p>
<pre><code class="hljspython">FROM openjdk:<span class="hljs-number">11</span>-jre <span class="hljs-keyword">as</span> builder
WORKDIR application
COPY target/*.jar application.jar
RUN java -Djarmode=layertools -jar application.jar extract

FROM openjdk:<span class="hljs-number">11</span>-jre
WORKDIR application
COPY --<span class="hljs-keyword">from</span>=builder application/dependencies/ ./
COPY --<span class="hljs-keyword">from</span>=builder application/spring-boot-loader/ ./
COPY --<span class="hljs-keyword">from</span>=builder application/snapshot-dependencies/ ./
COPY --<span class="hljs-keyword">from</span>=builder application/application/ ./
ENTRYPOINT [<span class="hljs-string">"java"</span>, <span class="hljs-string">"org.springframework.boot.loader.JarLauncher"</span>]
</code></pre>
</li>
</ol>
<h3 id="go">Go</h3>
<ol>
<li>
<p><strong>Build statically linked binaries</strong>:</p>
<pre><code class="hljsgo">FROM golang:<span class="hljs-number">1.21</span>-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=<span class="hljs-number">0</span> GOOS=linux <span class="hljs-keyword">go</span> build -a -installsuffix cgo -o main .

FROM scratch
COPY --from=builder /app/main /main
ENTRYPOINT [<span class="hljs-string">"/main"</span>]
</code></pre>
<p>This creates a binary with no external dependencies, allowing the use of a <code>scratch</code> image.</p>
</li>
<li>
<p><strong>Use Alpine for non-static binaries</strong>:
If you need CGO or can't use static linking:</p>
<pre><code class="hljsperl">FROM golang:<span class="hljs-number">1.21</span>-alpine AS builder
RUN apk add --<span class="hljs-keyword">no</span>-cache git
WORKDIR /app
COPY . .
RUN go build -o main .

FROM alpine:<span class="hljs-number">3.18</span>
RUN apk add --<span class="hljs-keyword">no</span>-cache ca-certificates
COPY --from=builder /app/main /main
ENTRYPOINT [<span class="hljs-string">"/main"</span>]
</code></pre>
</li>
</ol>
<p>By optimizing your Dockerfile to the specific needs of your programming language and application, you can create highly optimized Docker images that are both small in size and efficient in runtime.</p>
<h2 id="toc-8-regular-audits-and-updates">8. Regular Audits and Updates</h2>
<p>Regularly auditing and updating your Docker images is crucial for maintaining efficiency, security, and performance. Here's a detailed look at how to approach this:</p>
<ol>
<li>
<p><strong>Use Docker Scout for vulnerability scanning</strong>:
Docker Scout is a powerful tool for scanning your images for vulnerabilities:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">docker</span> <span class="hljs-selector-tag">scout</span> <span class="hljs-selector-tag">cve</span> <span class="hljs-selector-tag">your-image</span><span class="hljs-selector-pseudo">:tag</span>
</code></pre>
<p>This command provides a detailed report of known vulnerabilities in your image.</p>
</li>
<li>
<p><strong>Integrate scanning into your CI/CD pipeline</strong>:
Here's an example of how you might integrate Docker Scout into a GitHub Actions workflow:</p>
<pre><code class="hljsperl">name: Docker Image CI

on: [<span class="hljs-keyword">push</span>]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Build the Docker image
      run: docker build . --file Dockerfile --tag <span class="hljs-keyword">my</span>-image:$(date +%s)
    - name: Scan the Docker image
      run: docker scout cve <span class="hljs-keyword">my</span>-image:$(date +%s)
</code></pre>
</li>
<li>
<p><strong>Regularly update base images</strong>:
Set up a process to regularly check for and test updates to your base images. You can automate this with tools like Dependabot for GitHub.</p>
</li>
<li>
<p><strong>Monitor image size over time</strong>:
Keep track of your image sizes over time. You can do this manually or set up a script to record image sizes after each build:</p>
<pre><code class="hljsperl">docker images --<span class="hljs-keyword">format</span> <span class="hljs-string">"{{.Size}}\t{{.Repository}}:{{.Tag}}"</span> | <span class="hljs-keyword">sort</span> -h
</code></pre>
</li>
<li>
<p><strong>Analyze image layers</strong>:
Regularly inspect your image layers to identify any unexpected growth:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">docker</span> <span class="hljs-selector-tag">history</span> <span class="hljs-selector-tag">your-image</span><span class="hljs-selector-pseudo">:tag</span>
</code></pre>
</li>
<li>
<p><strong>Use image digests for immutability</strong>:
Instead of relying on tags, which can be overwritten, use image digests for important images:</p>
<pre><code class="hljsruby">docker pull your-image@sha256<span class="hljs-symbol">:a1b2c3</span>...
</code></pre>
</li>
<li>
<p><strong>Implement a retention policy</strong>:
Regularly clean up old and unused images to save storage space:</p>
<pre><code class="hljsgo">docker image prune -a --filter <span class="hljs-string">"until=240h"</span>
</code></pre>
<p>This removes images older than 10 days.</p>
</li>
<li>
<p><strong>Stay informed about Docker updates</strong>:
Keep your Docker engine and tools up to date, and stay informed about new features that might help optimize your workflows.</p>
</li>
<li>
<p><strong>Perform regular security audits</strong>:
Beyond just scanning for vulnerabilities, regularly review your Dockerfiles and build processes for security best practices.</p>
</li>
<li>
<p><strong>Test optimized images thoroughly</strong>:
After making optimizations, always thoroughly test your images to ensure they still function as expected in all required environments.</p>
</li>
</ol>
<p>By implementing these practices, you can ensure that your Docker images remain efficient, secure, and up-to-date over time.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Optimizing Docker image sizes is an ongoing process that requires attention to detail and regular reviews. By implementing these techniques, you can significantly reduce your Docker image sizes, leading to faster deployments, reduced costs, and improved security.</p>
<p>Remember, the goal is to find the right balance between image size and functionality. Don't sacrifice necessary features just to make your image smaller!</p>
<p>If you're looking to dive deeper into Docker and container optimization, don't forget to check out my <a href="https://devdojo.com/bobbyiliev/free-introduction-to-docker-ebook">free Introduction to Docker eBook</a>. It provides a solid foundation for working with Docker and will help you understand the concepts we've covered here in more depth.</p>
<p>Also, if you're setting up your Docker environment and need a reliable host, consider using DigitalOcean. You can get a <a href="https://m.do.co/c/2a9bba940f39">$200 free credit</a> to get started!</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>Happy Dockerizing! 🐳</p>
]]></description>
                                                            <category>docker</category>
                                            <category>devops</category>
                                            <category>cicd</category>
                                            <category>kubernetes</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12370</guid>
                <pubDate>Mon, 16 Sep 2024 01:51:52 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Laravel Announces Major Updates at Laracon US 2024: Cloud, Inertia 2.0, and More]]></title>
                <link>https://devdojo.com/bobbyiliev/laravel-announces-major-updates-at-laracon-us-2024-cloud-inertia-20-and-more</link>
                <description><![CDATA[<p>Hello Laravel community! 👋</p>
<p>The Laracon US 2024 keynote by Taylor Otwell just wrapped up, and there are some exciting new features coming to the Laravel ecosystem. Let's break down the key announcements!</p>
<p>You can watch the video here if you prefer too:</p>
<p><iframe width="100%" height="399" src="https://www.youtube.com/embed/AwWepVU5uWM" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
<h2 id="toc-1-laravel-cloud">1. Laravel Cloud ☁️</h2>
<p>Laravel Cloud is set to change how we deploy Laravel applications:</p>
<ul>
<li>🚀 Deploy from GitHub to a live URL in under a minute</li>
<li>🔄 Automatic scaling and load balancing</li>
<li>🐘 Serverless PostgreSQL databases that auto-scale</li>
<li>💤 App and database hibernation to reduce costs</li>
<li>🌿 Branch-based preview environments</li>
<li>🧑‍🔧 Integrated queue workers and job processing</li>
</ul>
<p>This platform aims to make deployment fast and worry-free for Laravel developers.</p>
<p>Larave Forge which we all love, will still remain if this is your thing!</p>
<h2 id="toc-2-inertia-2-0">2. Inertia 2.0 ✨</h2>
<p>Inertia.js is getting a major upgrade with version 2.0:</p>
<ul>
<li>🏎️ Async requests for better performance</li>
<li>🔄 Easy data polling</li>
<li>👀 "When visible" lazy loading</li>
<li>🛝 Helpers for infinite scrolling</li>
<li>🏃‍♂️ Page prefetching for faster navigation</li>
<li>🚚 Deferred props to optimize initial page loads</li>
</ul>
<p>These features will help create more responsive and faster-loading applications with Inertia.</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<h2 id="toc-3-official-vs-code-extension">3. Official VS Code Extension 💻</h2>
<p>I don't know about you but this is a huge thing for me! I wrote a post on the VS Code extensions that I am using and it will be great to update that post and just include that official VS Code extension!</p>
<blockquote>
<p><a href="https://devdojo.com/bobbyiliev/8-awesome-vs-code-extensions-for-laravel-developers">8 Awesome VS Code Extensions for Laravel Developers</a></p>
</blockquote>
<p>Laravel is releasing an official Visual Studio Code extension:</p>
<ul>
<li>🧠 Enhanced autocompletion for Laravel code</li>
<li>🕵️ Improved error detection</li>
<li>🗺️ Easy navigation between related files</li>
<li>🧪 Built-in testing tools</li>
</ul>
<p>This extension will make Laravel development in VS Code more efficient and user-friendly.</p>
<h2 id="toc-4-framework-updates">4. Framework Updates 🛠️</h2>
<p>The improvements to the Framework are worth of a totally separate post themselves! But some of the the Laravel framework new features that were announce were:</p>
<ul>
<li>🎯 Container attributes for simpler dependency injection</li>
<li>🏃‍♂️ Deferred functions for background processing without queues</li>
<li>🚦 Concurrency facade for parallel processing</li>
<li>🧊 Improved caching with flexible time-to-live options</li>
<li>🗄️ Local private file serving</li>
</ul>
<p>I love the new <code>deffered</code> functions! A game changer for simple tasks where you don't want to introduce jobs and queues.</p>
<p>These updates focus on improving performance and simplifying common tasks.</p>
<h2 id="toc-5-design-improvements">5. Design Improvements 🎨</h2>
<p>The Laravel team is also focusing on design:</p>
<ul>
<li>Refreshing the Laravel.com website</li>
<li>Creating a new brand resource site</li>
<li>Improving the documentation experience</li>
</ul>
<p>These changes aim to enhance the overall Laravel brand and make resources more accessible.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Laracon US 2024 has shown that Laravel continues to evolve and improve. From Laravel Cloud to Inertia 2.0, the VS Code extension, and core framework updates, there's a lot to look forward to in the Laravel ecosystem.</p>
<p>Keep an eye on the official Laravel channels for more information about release dates and how to get early access to Laravel Cloud.</p>
<p>What new feature are you most excited about? We'd love to hear your thoughts!</p>
]]></description>
                                                            <category>laravel</category>
                                            <category>laracon</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12326</guid>
                <pubDate>Fri, 30 Aug 2024 01:23:39 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Containerizing a  Microservices Architecture with Docker and Redpanda]]></title>
                <link>https://devdojo.com/bobbyiliev/containerizing-a-microservices-architecture-with-docker-and-redpanda</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Hello there!</p>
<p>I'm Bobby, a Docker Captain and the author of the <a href="https://devdojo.com/bobbyiliev/free-introduction-to-docker-ebook">free Introduction to Docker eBook</a>. In this guide, we'll dive deep into containerizing a microservices architecture using Docker and Redpanda, a Kafka-compatible event streaming platform.</p>
<p>As we progress through 2024, microservices and event-driven architectures continue to be at the leading approach of modern application development, and Docker remains an indispensable tool for deploying and managing these complex systems.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>Before we go any further, make sure that you have:</p>
<ul>
<li>Docker and Docker Compose installed on your system</li>
<li>Basic knowledge of Docker, microservices concepts, and event streaming</li>
</ul>
<p>If you're new to Docker or need a refresher, I highly recommend checking out my <a href="https://devdojo.com/bobbyiliev/free-introduction-to-docker-ebook">free Introduction to Docker eBook</a>. It covers all the fundamentals you'll need to follow along with this guide.</p>
<h2 id="our-example-architecture">Our Example Architecture</h2>
<p>For this article, we'll build a simple e-commerce platform with the following microservices:</p>
<ol>
<li>Product Service (Go)</li>
<li>User Service (Python)</li>
<li>Order Service (Node.js)</li>
<li>API Gateway (Nginx)</li>
<li>Event Streaming Platform (Redpanda)</li>
<li>Database (PostgreSQL)</li>
</ol>
<p>This architecture demonstrates a typical microservices setup, with each service responsible for a specific domain and communicating through both REST APIs and event streaming.</p>
<p>An example diagram of the architecture is shown below. The client apps (web, mobile, etc.) interact with the API Gateway, which routes requests to the appropriate microservices. The services communicate with a PostgreSQL database for persistent storage and with Redpanda for event streaming.</p>
<pre><code class="hljsruby">                  +-------------------+
                  <span class="hljs-params">|    Client Apps    |</span>
                  <span class="hljs-params">| (Web, Mobile, etc)|</span>
                  +---------+---------+
                            <span class="hljs-params">|
                            |</span> HTTP/HTTPS
                            <span class="hljs-params">|
                  +---------v---------+
                  |</span>                   <span class="hljs-params">|
                  |</span>    API Gateway    <span class="hljs-params">|
                  |</span>     (Nginx)       <span class="hljs-params">|
                  |</span>                   <span class="hljs-params">|
                  +----+------+-------+
                       |</span>      <span class="hljs-params">|
           +-----------+      +-----------+
           |</span>                              <span class="hljs-params">|
     +-----v------+  +-----------+  +-----v------+
     |</span>            <span class="hljs-params">|  |</span>           <span class="hljs-params">|  |</span>            <span class="hljs-params">|
     |</span>  Product   <span class="hljs-params">|  |</span>   User    <span class="hljs-params">|  |</span>   Order    <span class="hljs-params">|
     |</span>  Service   <span class="hljs-params">|  |</span>  Service  <span class="hljs-params">|  |</span>  Service   <span class="hljs-params">|
     |</span>   (Go)     <span class="hljs-params">|  |</span> (Python)  <span class="hljs-params">|  |</span> (Node.js)  <span class="hljs-params">|
     |</span>            <span class="hljs-params">|  |</span>           <span class="hljs-params">|  |</span>            <span class="hljs-params">|
     +-----+------+  +-----+-----+  +-----+------+
           |</span>              <span class="hljs-params">|              |</span>
           <span class="hljs-params">|              |</span>              <span class="hljs-params">|
    +------v--------------v--------------v------+
    |</span>                                           <span class="hljs-params">|
    |</span>              PostgreSQL                   <span class="hljs-params">|
    |</span>              Database                     <span class="hljs-params">|
    |</span>                                           <span class="hljs-params">|
    +-------------------------------------------+
                       |</span>
                       <span class="hljs-params">| (Event Streaming)
                       |</span>
               +-------v-------+
               <span class="hljs-params">|               |</span>
               <span class="hljs-params">|   Redpanda    |</span>
               <span class="hljs-params">|               |</span>
               +-------+-------+
                       <span class="hljs-params">|
                       |</span> (External Systems)
                       <span class="hljs-params">|
               +-------v-------+
               |</span> Notifications <span class="hljs-params">|
               |</span>   Analytics   <span class="hljs-params">|
               |</span> Materialize   <span class="hljs-params">|
               |</span> Other Services<span class="hljs-params">|
               +---------------+
</span></code></pre>
<h2 id="step-1-dockerizing-individual-services">Step 1: Dockerizing Individual Services</h2>
<p>Let's start by creating <a href="https://docs.docker.com/reference/dockerfile/">Dockerfiles</a> for each service and include some basic code examples. We'll go through each service, explaining the Dockerfile and the code in detail.</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<h3 id="product-service-go">Product Service (Go)</h3>
<p>Dockerfile:</p>
<pre><code class="hljsgo">FROM golang:<span class="hljs-number">1.21</span>-alpine AS builder
WORKDIR /app
COPY . .
RUN <span class="hljs-keyword">go</span> build -o main .

FROM alpine:<span class="hljs-number">3.18</span>
COPY --from=builder /app/main /app/main
CMD [<span class="hljs-string">"/app/main"</span>]
</code></pre>
<p>This Dockerfile uses a multi-stage build:</p>
<ol>
<li>The first stage uses the <code>golang:1.21-alpine</code> image to build our Go application.</li>
<li>The second stage uses a minimal <code>alpine:3.18</code> image and copies only the compiled binary from the first stage.</li>
<li>This approach significantly reduces the final image size by excluding all build tools and intermediate files.</li>
</ol>
<p>Basic Go code (main.go):</p>
<pre><code class="hljsgo"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"encoding/json"</span>
    <span class="hljs-string">"log"</span>
    <span class="hljs-string">"net/http"</span>
)

<span class="hljs-keyword">type</span> Product <span class="hljs-keyword">struct</span> {
    ID    <span class="hljs-keyword">string</span>  <span class="hljs-string">`json:"id"`</span>
    Name  <span class="hljs-keyword">string</span>  <span class="hljs-string">`json:"name"`</span>
    Price <span class="hljs-keyword">float64</span> <span class="hljs-string">`json:"price"`</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    http.HandleFunc(<span class="hljs-string">"/products"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
        products := []Product{
            {ID: <span class="hljs-string">"1"</span>, Name: <span class="hljs-string">"Product 1"</span>, Price: <span class="hljs-number">19.99</span>},
            {ID: <span class="hljs-string">"2"</span>, Name: <span class="hljs-string">"Product 2"</span>, Price: <span class="hljs-number">29.99</span>},
        }
        json.NewEncoder(w).Encode(products)
    })

    log.Fatal(http.ListenAndServe(<span class="hljs-string">":8080"</span>, <span class="hljs-literal">nil</span>))
}
</code></pre>
<p>This Go code:</p>
<ol>
<li>Defines a simple <code>Product</code> struct.</li>
<li>Creates an HTTP server that listens on port 8080.</li>
<li>Implements a <code>/products</code> endpoint that returns a JSON array of product data.</li>
<li>In a real-world scenario, this would interact with a database instead of using hardcoded data but for simplicity, we're using hardcoded data.</li>
</ol>
<h3 id="user-service-python">User Service (Python)</h3>
<p>Dockerfile:</p>
<pre><code class="hljsperl">FROM python:<span class="hljs-number">3.11</span>-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --<span class="hljs-keyword">no</span>-cache-dir -r requirements.txt
COPY . .
CMD [<span class="hljs-string">"python"</span>, <span class="hljs-string">"app.py"</span>]
</code></pre>
<p>This Dockerfile:</p>
<ol>
<li>Uses the official Python 3.11 slim image as the base.</li>
<li>Sets the working directory to <code>/app</code>.</li>
<li>Copies and installs the Python dependencies first (for better caching).</li>
<li>Copies the rest of the application code.</li>
<li>Specifies the command to run the Python application.</li>
</ol>
<p>Basic Python code (app.py):</p>
<pre><code class="hljspython"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, jsonify

app = Flask(__name__)

<span class="hljs-meta">@app.route('/users')</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_users</span><span class="hljs-params">()</span>:</span>
    users = [
        {<span class="hljs-string">"id"</span>: <span class="hljs-string">"1"</span>, <span class="hljs-string">"name"</span>: <span class="hljs-string">"John Doe"</span>},
        {<span class="hljs-string">"id"</span>: <span class="hljs-string">"2"</span>, <span class="hljs-string">"name"</span>: <span class="hljs-string">"Jane Smith"</span>}
    ]
    <span class="hljs-keyword">return</span> jsonify(users)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    app.run(host=<span class="hljs-string">'0.0.0.0'</span>, port=<span class="hljs-number">8081</span>)
</code></pre>
<p>This Python code:</p>
<ol>
<li>Uses Flask to create a simple web server.</li>
<li>Defines a <code>/users</code> endpoint that returns a JSON array of user data.</li>
<li>Runs the server on port 8081 and binds to all available network interfaces (<code>0.0.0.0</code>).</li>
<li>Like the product service, in a real-world scenario, this would interact with a database.</li>
</ol>
<h3 id="order-service-node-js">Order Service (Node.js)</h3>
<p>Dockerfile:</p>
<pre><code class="hljsgo">FROM node:<span class="hljs-number">20</span>-alpine
WORKDIR /app
COPY <span class="hljs-keyword">package</span>*.json ./
RUN npm ci --only=production
COPY . .
CMD [<span class="hljs-string">"node"</span>, <span class="hljs-string">"server.js"</span>]
</code></pre>
<p>This Dockerfile:</p>
<ol>
<li>Uses the official Node.js 20 Alpine image as the base.</li>
<li>Sets the working directory to <code>/app</code>.</li>
<li>Copies package.json and package-lock.json files first.</li>
<li>Installs production dependencies using <code>npm ci</code> for faster, more reliable builds.</li>
<li>Copies the rest of the application code.</li>
<li>Specifies the command to run the Node.js application.</li>
</ol>
<p>Basic Node.js code (server.js):</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">const</span> { Kafka } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'kafkajs'</span>);

<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> kafka = <span class="hljs-keyword">new</span> Kafka({
  <span class="hljs-attr">clientId</span>: <span class="hljs-string">'order-service'</span>,
  <span class="hljs-attr">brokers</span>: [<span class="hljs-string">'redpanda:9092'</span>]
});

<span class="hljs-keyword">const</span> producer = kafka.producer();

app.use(express.json());

app.post(<span class="hljs-string">'/orders'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> order = req.body;
  <span class="hljs-keyword">await</span> producer.connect();
  <span class="hljs-keyword">await</span> producer.send({
    <span class="hljs-attr">topic</span>: <span class="hljs-string">'orders'</span>,
    <span class="hljs-attr">messages</span>: [{ <span class="hljs-attr">value</span>: <span class="hljs-built_in">JSON</span>.stringify(order) }],
  });
  <span class="hljs-keyword">await</span> producer.disconnect();
  res.status(<span class="hljs-number">201</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Order created'</span> });
});

app.listen(<span class="hljs-number">8082</span>, () =&gt; <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Order service listening on port 8082'</span>));
</code></pre>
<p>This Node.js code:</p>
<ol>
<li>Uses Express to create a web server.</li>
<li>Configures a Kafka producer to connect to our Redpanda service.</li>
<li>Implements a POST <code>/orders</code> endpoint that:
<ul>
<li>Receives order data in the request body.</li>
<li>Sends the order data to a Kafka topic named 'orders'.</li>
<li>Responds with a success message.</li>
</ul>
</li>
<li>This service demonstrates how to integrate with Redpanda for event streaming.</li>
</ol>
<h2 id="step-2-creating-the-docker-compose-file">Step 2: Creating the Docker Compose File</h2>
<p>Now, let's create a <code>compose.yaml</code> file to orchestrate our microservices. We'll go through each section of the Compose file in detail:</p>
<pre><code class="hljsmarkdown">version: '3.9'

services:
  product-service:
<span class="hljs-code">    build: ./product-service</span>
<span class="hljs-code">    ports:</span>
<span class="hljs-bullet">      - </span>"8080:8080"
<span class="hljs-code">    environment:</span>
<span class="hljs-bullet">      - </span>DB_HOST=postgres
<span class="hljs-code">    depends_on:</span>
<span class="hljs-bullet">      - </span>postgres

  user-service:
<span class="hljs-code">    build: ./user-service</span>
<span class="hljs-code">    ports:</span>
<span class="hljs-bullet">      - </span>"8081:8081"
<span class="hljs-code">    environment:</span>
<span class="hljs-bullet">      - </span>DB_HOST=postgres
<span class="hljs-code">    depends_on:</span>
<span class="hljs-bullet">      - </span>postgres

  order-service:
<span class="hljs-code">    build: ./order-service</span>
<span class="hljs-code">    ports:</span>
<span class="hljs-bullet">      - </span>"8082:8082"
<span class="hljs-code">    environment:</span>
<span class="hljs-bullet">      - </span>DB_HOST=postgres
<span class="hljs-bullet">      - </span>REDPANDA_HOST=redpanda
<span class="hljs-code">    depends_on:</span>
<span class="hljs-bullet">      - </span>postgres
<span class="hljs-bullet">      - </span>redpanda

  api-gateway:
<span class="hljs-code">    image: nginx:alpine</span>
<span class="hljs-code">    ports:</span>
<span class="hljs-bullet">      - </span>"80:80"
<span class="hljs-code">    volumes:</span>
<span class="hljs-bullet">      - </span>./nginx.conf:/etc/nginx/nginx.conf:ro
<span class="hljs-code">    depends_on:</span>
<span class="hljs-bullet">      - </span>product-service
<span class="hljs-bullet">      - </span>user-service
<span class="hljs-bullet">      - </span>order-service

  postgres:
<span class="hljs-code">    image: postgres:13-alpine</span>
<span class="hljs-code">    environment:</span>
<span class="hljs-code">      POSTGRES_PASSWORD: mysecretpassword</span>
<span class="hljs-code">    volumes:</span>
<span class="hljs-bullet">      - </span>pgdata:/var/lib/postgresql/data

  redpanda:
<span class="hljs-code">    container_name: redpanda</span>
<span class="hljs-code">    image: docker.vectorized.io/vectorized/redpanda:v23.3.5</span>
<span class="hljs-code">    depends_on:</span>
<span class="hljs-bullet">      - </span>postgres
<span class="hljs-code">    command:</span>
<span class="hljs-bullet">      - </span>redpanda start
<span class="hljs-bullet">      - </span>--overprovisioned
<span class="hljs-bullet">      - </span>--smp 1
<span class="hljs-bullet">      - </span>--memory 1G
<span class="hljs-bullet">      - </span>--reserve-memory 0M
<span class="hljs-bullet">      - </span>--node-id 0
<span class="hljs-bullet">      - </span>--check=false
<span class="hljs-bullet">      - </span>--kafka-addr 0.0.0.0:9092
<span class="hljs-bullet">      - </span>--advertise-kafka-addr redpanda:9092
<span class="hljs-bullet">      - </span>--pandaproxy-addr 0.0.0.0:8082
<span class="hljs-bullet">      - </span>--advertise-pandaproxy-addr redpanda:8082
<span class="hljs-bullet">      - </span>--set redpanda.enable_transactions=true
<span class="hljs-bullet">      - </span>--set redpanda.enable_idempotence=true
<span class="hljs-bullet">      - </span>--set redpanda.auto<span class="hljs-emphasis">_create_</span>topics_enabled=true
<span class="hljs-bullet">      - </span>--set redpanda.default<span class="hljs-emphasis">_topic_</span>partitions=1
<span class="hljs-code">    ports:</span>
<span class="hljs-bullet">      - </span>9092:9092
<span class="hljs-bullet">      - </span>8081:8081
<span class="hljs-bullet">      - </span>8082:8082
<span class="hljs-code">    healthcheck:</span>
<span class="hljs-code">      test: curl -f localhost:9644/v1/status/ready</span>
<span class="hljs-code">      interval: 1s</span>
<span class="hljs-code">      start_period: 30s</span>

volumes:
  pgdata:
</code></pre>
<p>Let's break down the key components of this Compose file:</p>
<ol>
<li>
<p><strong>Version</strong>: We're using version 3.9 of the Compose file format, which is compatible with Docker Engine 19.03.0+.</p>
</li>
<li>
<p><strong>Services</strong>:</p>
<ul>
<li>Each service is defined under the <code>services</code> key.</li>
<li>For our custom services (product, user, order), we use the <code>build</code> directive to specify the location of the Dockerfile.</li>
<li>We map ports from the container to the host for each service.</li>
<li>Environment variables are set using the <code>environment</code> key.</li>
<li>Dependencies between services are specified using <code>depends_on</code>.</li>
</ul>
</li>
<li>
<p><strong>API Gateway</strong>:</p>
<ul>
<li>Uses the official Nginx Alpine image.</li>
<li>Mounts a custom Nginx configuration file.</li>
<li>Depends on all other services to ensure they're running before the gateway starts.</li>
</ul>
</li>
<li>
<p><strong>PostgreSQL</strong>:</p>
<ul>
<li>Uses the official PostgreSQL Alpine image.</li>
<li>Sets a password using an environment variable.</li>
<li>Uses a named volume for data persistence.</li>
</ul>
</li>
<li>
<p><strong>Redpanda</strong>:</p>
<ul>
<li>Uses the official Redpanda image.</li>
<li>Configures various Redpanda-specific options.</li>
<li>Exposes necessary ports for Kafka protocol and admin interface.</li>
<li>Includes a health check to ensure the service is ready.</li>
</ul>
</li>
<li>
<p><strong>Volumes</strong>:</p>
<ul>
<li>Defines a named volume <code>pgdata</code> for PostgreSQL data persistence. This volume is automatically created by Docker and will persist even if the container is removed or recreated.</li>
</ul>
</li>
</ol>
<h2 id="step-3-configuring-the-api-gateway">Step 3: Configuring the API Gateway</h2>
<p>The API Gateway uses Nginx to route requests to the appropriate microservices. Here's a detailed explanation of the <code>nginx.conf</code> file:</p>
<pre><code class="hljsgo">events {
    worker_connections <span class="hljs-number">1024</span>;
}

http {
    upstream product-service {
        server product-service:<span class="hljs-number">8080</span>;
    }

    upstream user-service {
        server user-service:<span class="hljs-number">8081</span>;
    }

    upstream order-service {
        server order-service:<span class="hljs-number">8082</span>;
    }

    server {
        listen <span class="hljs-number">80</span>;

        location /products {
            proxy_pass http:<span class="hljs-comment">//product-service;</span>
        }

        location /users {
            proxy_pass http:<span class="hljs-comment">//user-service;</span>
        }

        location /orders {
            proxy_pass http:<span class="hljs-comment">//order-service;</span>
        }
    }
}
</code></pre>
<p>This Nginx configuration:</p>
<ol>
<li>Defines upstream servers for each of our microservices.</li>
<li>Creates a server block that listens on port 80.</li>
<li>Uses location blocks to route requests to the appropriate service based on the URL path.</li>
<li>This setup allows us to access all our microservices through a single entry point.</li>
</ol>
<h2 id="step-4-running-the-microservices">Step 4: Running the Microservices</h2>
<p>To start all services, use the following command:</p>
<pre><code class="hljs">docker compose up -d
</code></pre>
<p>This command:</p>
<ol>
<li>Builds any images that aren't cached (on the first run or when changes are made).</li>
<li>Creates and starts containers for all services defined in the Compose file.</li>
<li>The <code>-d</code> flag runs the containers in detached mode (in the background).</li>
</ol>
<p>To view the logs of all running containers:</p>
<pre><code class="hljspowershell">docker compose logs <span class="hljs-operator">-f</span>
</code></pre>
<p>The <code>-f</code> flag "follows" the log output, showing logs in real-time.</p>
<p>To stop all services:</p>
<pre><code class="hljs">docker compose down
</code></pre>
<p>This stops and removes all containers defined in the Compose file. Add the <code>-v</code> flag to also remove volumes.</p>
<h2 id="step-5-testing-the-services">Step 5: Testing the Services</h2>
<p>You can test the services using curl or a tool like Postman. Here are some example curl commands:</p>
<ol>
<li>
<p>Get products:</p>
<pre><code class="hljsgo">curl http:<span class="hljs-comment">//localhost/products</span>
</code></pre>
<p>This should return a JSON array of products.</p>
</li>
<li>
<p>Get users:</p>
<pre><code class="hljsgo">curl http:<span class="hljs-comment">//localhost/users</span>
</code></pre>
<p>This should return a JSON array of users.</p>
</li>
<li>
<p>Create an order:</p>
<pre><code class="hljsperl">curl -X POST http:<span class="hljs-regexp">//localhost</span><span class="hljs-regexp">/orders -H "Content-Type: application/json</span><span class="hljs-string">" -d '{"</span>productId<span class="hljs-string">": "</span><span class="hljs-number">1</span><span class="hljs-string">", "</span>userId<span class="hljs-string">": "</span><span class="hljs-number">1</span><span class="hljs-string">", "</span>quantity<span class="hljs-string">": 2}'
</span></code></pre>
<p>This should return a success message and send the order data to Redpanda.</p>
</li>
</ol>
<h2 id="step-6-scaling-services">Step 6: Scaling Services</h2>
<p>One of the advantages of this architecture is the ability to scale services independently. To scale a service, use:</p>
<pre><code class="hljs">docker compose up -d --scale product-service=3
</code></pre>
<p>This command:</p>
<ol>
<li>Increases the number of product-service containers to 3.</li>
<li>The API Gateway (Nginx) will automatically load balance requests between these instances.</li>
</ol>
<p>You can verify the running containers with:</p>
<pre><code class="hljs">docker compose ps
</code></pre>
<h2 id="step-7-monitoring-and-logging">Step 7: Monitoring and Logging</h2>
<p>For a production environment, you'd want to add monitoring and centralized logging. Here's a basic example of adding Prometheus for monitoring:</p>
<p>Add this to your <code>compose.yaml</code>:</p>
<pre><code class="hljsmarkdown">  prometheus:
<span class="hljs-code">    image: prom/prometheus</span>
<span class="hljs-code">    ports:</span>
<span class="hljs-bullet">      - </span>"9090:9090"
<span class="hljs-code">    volumes:</span>
<span class="hljs-bullet">      - </span>./prometheus.yml:/etc/prometheus/prometheus.yml
</code></pre>
<p>Create a <code>prometheus.yml</code> file:</p>
<pre><code class="hljspython"><span class="hljs-keyword">global</span>:
  scrape_interval: <span class="hljs-number">15</span>s

scrape_configs:
  - job_name: <span class="hljs-string">'prometheus'</span>
    static_configs:
      - targets: [<span class="hljs-string">'localhost:9090'</span>]

  - job_name: <span class="hljs-string">'redpanda'</span>
    static_configs:
      - targets: [<span class="hljs-string">'redpanda:9644'</span>]
</code></pre>
<p>This setup will allow Prometheus to scrape metrics from itself and Redpanda.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Docker Compose is awesome for local development environments and cross-team collaboration by providing a unified, easily shareable configuration that ensures consistent setup across different machines, allowing developers to quickly spin up the entire microservices ecosystem with a single command, regardless of their individual development environments. For production deployments, you'd typically use a container orchestration platform like Kubernetes or Docker Swarm.</p>
<p>If you're looking to dive deeper into Docker and container orchestration, don't forget to check out my <a href="https://devdojo.com/bobbyiliev/free-introduction-to-docker-ebook">free Introduction to Docker eBook</a>. It provides a solid foundation for working with Docker and will help you understand the concepts we've covered here in more depth.</p>
<p>Also, if you're setting up your containerized microservices and need a reliable host, consider using DigitalOcean. You can get a <a href="https://m.do.co/c/2a9bba940f39">$200 free credit</a> to get started!</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>Happy Dockerizing, and may your microservices always be scalable, resilient, and secure!</p>
]]></description>
                                                            <category>docker</category>
                                            <category>devops</category>
                                            <category>redpanda</category>
                                            <category>docker compose</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12318</guid>
                <pubDate>Wed, 28 Aug 2024 06:30:53 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[5 Docker Best Practices I Wish I Knew When I Started]]></title>
                <link>https://devdojo.com/bobbyiliev/5-docker-best-practices-i-wish-i-knew-when-i-started</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Hey! I'm Bobby, a Docker Captain and the author of the <a href="https://devdojo.com/bobbyiliev/free-introduction-to-docker-ebook">free Introduction to Docker eBook</a>.</p>
<p>In this article, I'll share five Docker best practices that I wish I knew when I first started using Docker. These tips will help you avoid common mistakes and improve your Docker workflows, making your containerization journey smoother and more efficient.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>To follow along, you should have:</p>
<ul>
<li>Basic knowledge of Docker</li>
<li>Docker installed on your system</li>
</ul>
<p>If you're new to Docker, I encourage you to check out my <a href="https://devdojo.com/bobbyiliev/free-introduction-to-docker-ebook">free Introduction to Docker eBook</a>. It covers all the basics you need to get started with Docker and will provide a solid foundation for understanding the best practices we'll discuss in this article.</p>
<p></p><div style="width:100%;height:0;padding-bottom:56%;position:relative;display:block"><iframe src="https://giphy.com/embed/QJvwBSGaoc4eI" width="100%" height="100%" style="position:absolute" frameborder="0" class="giphy-embed" allowfullscreen></iframe></div>
<h2 id="step-1-use-multi-stage-builds-for-smaller-images">Step 1 — Use Multi-Stage Builds for Smaller Images</h2>
<p><a href="https://docs.docker.com/build/building/multi-stage/">Multi-stage builds</a> are a powerful feature in Docker that help you create smaller, more efficient Docker images. This is crucial because smaller images offer several advantages:</p>
<ol>
<li>They're faster to push and pull from registries, reducing deployment times.</li>
<li>They use less storage space, which can lead to cost savings in cloud environments.</li>
<li>They have a smaller attack surface, potentially improving security.</li>
</ol>
<p></p><div style="width:100%;height:0;padding-bottom:56%;position:relative;display:block"><iframe src="https://giphy.com/embed/1nR6fu93A17vWZbO9c" width="100%" height="100%" style="position:absolute" frameborder="0" class="giphy-embed" allowfullscreen></iframe></div>
<p>Here's an example of a multi-stage Dockerfile for a Go application:</p>
<pre><code class="hljspython"><span class="hljs-comment"># Build stage</span>
FROM golang:<span class="hljs-number">1.16</span> AS builder
WORKDIR /app
COPY . .
RUN go build -o main .

<span class="hljs-comment"># Final stage</span>
FROM alpine:latest
WORKDIR /root/
COPY --<span class="hljs-keyword">from</span>=builder /app/main .
CMD [<span class="hljs-string">"./main"</span>]
</code></pre>
<p>Let's break this down:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<ol>
<li>
<p>The first stage, labeled as <code>builder</code>, uses the official Go image to compile our application. This stage includes all the necessary build tools and dependencies.</p>
</li>
<li>
<p>The second stage starts from a minimal Alpine Linux image. We copy only the compiled binary from the <code>builder</code> stage using the <code>COPY --from=builder</code> command.</p>
</li>
<li>
<p>The final image contains only the compiled application and the minimal runtime requirements, resulting in a much smaller image.</p>
</li>
</ol>
<p>By using this approach, you can significantly reduce your image size. For example, a Go application image could shrink from several hundred megabytes to just a few megabytes. This not only saves space but also reduces the time it takes to deploy your application.</p>
<h2 id="step-2-use-dockerignore-files">Step 2 — Use <code>.dockerignore</code> Files</h2>
<p>A <code>.dockerignore</code> file is to Docker what a <code>.gitignore</code> file is to Git. It helps you exclude files and directories from your Docker build context. This is important for several reasons:</p>
<ol>
<li>It speeds up the build process by reducing the amount of data sent to the Docker daemon.</li>
<li>It prevents sensitive or unnecessary files from being inadvertently included in your Docker image.</li>
<li>It helps reduce the final size of your Docker image.</li>
</ol>
<p>Here's an example of a <code>.dockerignore</code> file:</p>
<pre><code class="hljscss"><span class="hljs-selector-class">.git</span>
*<span class="hljs-selector-class">.md</span>
*<span class="hljs-selector-class">.log</span>
<span class="hljs-selector-tag">node_modules</span>
<span class="hljs-selector-tag">test</span>
</code></pre>
<p>Let's go through each line:</p>
<ul>
<li><code>.git</code>: Excludes the entire Git repository, which is usually not needed in the Docker image.</li>
<li><code>*.md</code>: Ignores all Markdown files, typically documentation that's not required for running the application.</li>
<li><code>*.log</code>: Prevents any log files from being included in the image.</li>
<li><code>node_modules</code>: For Node.js projects, this excludes all dependencies, which should be installed fresh during the build process.</li>
<li><code>test</code>: Excludes the test directory, as tests typically aren't needed in production images.</li>
</ul>
<p>By using a <code>.dockerignore</code> file, your Docker builds will be faster and your images will be cleaner and more secure. It's a simple step that can make a big difference in your Docker workflow.</p>
<p>For more information check out the <a href="https://docs.docker.com/reference/dockerfile/#dockerignore-file">official <code>.dockerignore</code> documentation</a>.</p>
<h2 id="step-3-implement-health-checks-in-your-dockerfiles">Step 3 — Implement Health Checks in Your <code>Dockerfiles</code></h2>
<p><a href="https://docs.docker.com/reference/dockerfile/#healthcheck">Health checks</a> are an important feature in Docker that help you make sure that your containers are not only running, but actually working as expected. They allow Docker to regularly check if your application is functioning correctly.</p>
<p></p><div style="width:100%;height:0;padding-bottom:56%;position:relative;display:block"><iframe src="https://giphy.com/embed/9Ai5dIk8xvBm0" width="100%" height="100%" style="position:absolute" frameborder="0" class="giphy-embed" allowfullscreen></iframe></div>
<p>Here's an example of how to add a health check to a Dockerfile for an Nginx server:</p>
<pre><code class="hljsperl">FROM nginx:latest
HEALTHCHECK --interval=<span class="hljs-number">30</span><span class="hljs-keyword">s</span> --timeout=<span class="hljs-number">3</span><span class="hljs-keyword">s</span> --start-period=<span class="hljs-number">5</span><span class="hljs-keyword">s</span> --retries=<span class="hljs-number">3</span> \
  CMD curl -f http:<span class="hljs-regexp">//localhost</span><span class="hljs-regexp">/ || exit 1
</span></code></pre>
<p>Let's break down the health check options:</p>
<ul>
<li><code>--interval=30s</code>: Docker will run the health check every 30 seconds.</li>
<li><code>--timeout=3s</code>: The health check must complete within 3 seconds, or it's considered failed.</li>
<li><code>--start-period=5s</code>: Docker will wait 5 seconds before running the first health check, giving the application time to start up.</li>
<li><code>--retries=3</code>: If the health check fails 3 times in a row, the container is considered unhealthy.</li>
</ul>
<p>The actual health check command (<code>curl -f http://localhost/ || exit 1</code>) attempts to make an HTTP request to the server. If the request fails, the health check fails.</p>
<p>Implementing health checks offers several benefits:</p>
<ol>
<li>It allows Docker to automatically restart containers that have become unhealthy.</li>
<li>In a swarm or orchestrated environment, it enables automatic rerouting of traffic away from unhealthy containers.</li>
<li>It provides a clear indicator of application health, making it easier to monitor and troubleshoot issues.</li>
</ol>
<p>By implementing health checks, you're adding an extra layer of reliability to your containerized applications.</p>
<h2 id="step-4-use-docker-compose-for-local-development">Step 4 — Use Docker Compose for Local Development</h2>
<p><a href="https://docs.docker.com/compose/">Docker Compose</a> is a tool for defining and running multi-container Docker applications. It's especially useful for local development environments where you might need to run several interconnected services.</p>
<p>As of recent updates to Docker, the default path for a Compose file is <code>compose.yaml</code> (preferred) or <code>compose.yml</code> in the working directory. Docker Compose also supports <code>docker-compose.yaml</code> and <code>docker-compose.yml</code> for backwards compatibility. If both files exist, Compose prefers the canonical <code>compose.yaml</code>.</p>
<p>Here's an example of a <code>compose.yaml</code> file:</p>
<pre><code class="hljsmarkdown">version: '3'
services:
  web:
<span class="hljs-code">    build: .</span>
<span class="hljs-code">    ports:</span>
<span class="hljs-bullet">      - </span>"8000:8000"
<span class="hljs-code">    volumes:</span>
<span class="hljs-bullet">      - </span>.:/code
<span class="hljs-code">    environment:</span>
<span class="hljs-bullet">      - </span>DEBUG=True
  db:
<span class="hljs-code">    image: postgres:13</span>
<span class="hljs-code">    environment:</span>
<span class="hljs-bullet">      - </span>POSTGRES_DB=myapp
<span class="hljs-bullet">      - </span>POSTGRES_PASSWORD=secret
</code></pre>
<p>This <code>compose.yaml</code> file defines two services:</p>
<ol>
<li>
<p><code>web</code>: This is your application. It builds from the current directory (<code>.</code>), maps port 8000, mounts the current directory as a volume for live code reloading, and sets an environment variable.</p>
</li>
<li>
<p><code>db</code>: This is a PostgreSQL database using the official image. It sets up a database named <code>myapp</code> with a password.</p>
</li>
</ol>
<p>To run this multi-container setup, you simply use:</p>
<pre><code class="hljs">docker compose up
</code></pre>
<p>Docker Compose offers several advantages for local development:</p>
<ol>
<li>It allows you to define your entire application stack in a single file.</li>
<li>You can start all services with a single command.</li>
<li>It provides an isolated environment that closely mimics production.</li>
<li>It's easy to add or remove services as your application evolves.</li>
</ol>
<p>By using Docker Compose, you can significantly simplify your development workflow and ensure consistency across your team.</p>
<h2 id="step-5-be-cautious-with-the-latest-tag">Step 5 — Be Cautious with the Latest Tag</h2>
<p>While using the <code>latest</code> tag might seem convenient, it can lead to unexpected issues and make your builds less reproducible. Here's why you should be cautious with the <code>latest</code> tag:</p>
<ol>
<li>Unpredictability: The <code>latest</code> tag doesn't refer to a specific version. It usually points to the most recent version, which can change without warning.</li>
<li>Lack of consistency: Different team members or environments might pull the <code>latest</code> image at different times, potentially getting different versions.</li>
<li>Difficulties in debugging: If an issue arises, it's harder to pinpoint which exact version of the image is causing the problem.</li>
</ol>
<p>Instead of using <code>latest</code>, it's better to specify exact versions in your Dockerfile or compose file. For example:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">FROM</span> <span class="hljs-selector-tag">node</span><span class="hljs-selector-pseudo">:18.20.4</span>
</code></pre>
<p>Or in your <code>compose.yaml</code>:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">services</span>:
  <span class="hljs-selector-tag">db</span>:
    <span class="hljs-selector-tag">image</span>: <span class="hljs-selector-tag">postgres</span><span class="hljs-selector-pseudo">:13.3</span>
</code></pre>
<p>By specifying exact versions:</p>
<ol>
<li>You ensure that everyone on your team is using the same version.</li>
<li>You can easily reproduce builds and deployments.</li>
<li>You have more control over when and how you update your dependencies.</li>
</ol>
<p>When you're ready to update to a new version, you can do so intentionally by changing the version number in your Dockerfile or compose file. This gives you the opportunity to test the new version thoroughly before deploying it to production.</p>
<h2 id="bonus-tip-implement-regular-security-scans">Bonus Tip: Implement Regular Security Scans</h2>
<p>Security should be a top priority when working with Docker images. Implementing regular security scans can help you identify and address vulnerabilities in your containers before they become a problem.</p>
<p>One powerful tool for this purpose is <a href="https://docs.docker.com/scout/">Docker Scout</a>, which is integrated directly into Docker Desktop and the Docker CLI. Here's how you can use it:</p>
<ol>
<li>
<p>First, ensure you have the latest version of Docker installed.</p>
</li>
<li>
<p>To scan an image, use the following command:</p>
<pre><code class="hljsxml">docker scout cve <span class="hljs-tag">&lt;<span class="hljs-name">image_name</span>&gt;</span>
</code></pre>
<p>For example:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">docker</span> <span class="hljs-selector-tag">scout</span> <span class="hljs-selector-tag">cve</span> <span class="hljs-selector-tag">nginx</span><span class="hljs-selector-pseudo">:latest</span>
</code></pre>
</li>
<li>
<p>This command will provide a detailed report of any known vulnerabilities in the image, including severity levels and available fixes.</p>
</li>
</ol>
<p>You can also integrate Docker Scout into your CI/CD pipeline to automatically scan images before deployment. Here's an example of how you might do this in a GitHub Actions workflow:</p>
<pre><code class="hljsperl">name: Docker Image CI

on: [<span class="hljs-keyword">push</span>]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Build the Docker image
      run: docker build . --file Dockerfile --tag <span class="hljs-keyword">my</span>-image:$(date +%s)
    - name: Scan the Docker image
      run: docker scout cve <span class="hljs-keyword">my</span>-image:$(date +%s)
</code></pre>
<p>By implementing regular security scans, you can:</p>
<ol>
<li>Identify vulnerabilities early in your development process</li>
<li>Keep your production environments more secure</li>
<li>Stay informed about the security status of your Docker images</li>
<li>Make informed decisions about when to update your base images or dependencies</li>
</ol>
<p>Regular scans, combined with prompt updates and patches, will help keep your Docker environments secure.</p>
<h2 id="conclusion">Conclusion</h2>
<p>These tips - using multi-stage builds, leveraging .dockerignore files, implementing health checks, using Docker Compose for local development, being cautious with the latest tag and running security scans - will help you create more efficient, reliable, and maintainable Docker workflows.</p>
<p>If you're looking to dive deeper into Docker, I encourage you to check out my <a href="https://devdojo.com/bobbyiliev/free-introduction-to-docker-ebook">free Introduction to Docker eBook</a>.</p>
<p>Also, if you're setting up your Docker environment and need a reliable host, consider using DigitalOcean. You can get a <a href="https://m.do.co/c/2a9bba940f39">$200 free credit</a> to get started!</p>
<p>Happy Dockerizing, and may your containers always run smoothly!</p>
<p>- Bobby</p>
]]></description>
                                                            <category>docker</category>
                                            <category>devops</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12306</guid>
                <pubDate>Fri, 23 Aug 2024 08:32:07 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to Remove Arrow on Input type Number with Tailwind CSS]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-remove-arrow-on-input-type-number-with-tailwind-css</link>
                <description><![CDATA[<p>When designing forms with Tailwind CSS, you might want to remove the default arrows (also known as spinners) from number input fields. These arrows can interfere with custom designs and are challenging to style consistently across different browsers.</p>
<p>In this tutorial, we'll explore how to achieve this using Tailwind CSS, both with inline styles and through a global CSS approach. We'll also address accessibility concerns and provide examples for styling custom buttons.</p>
<h2 id="the-problem">The Problem</h2>
<p>By default, browsers add increment and decrement arrows to <code>&lt;input type="number"&gt;</code> elements. While functional, these arrows often clash with custom designs and can be difficult to style uniformly across various browsers.</p>
<p><img src="https://imgur.com/k8QXjuc.png" alt="Default number input with arrows"></p>
<h2 id="the-solution">The Solution</h2>
<p>We'll use Tailwind CSS utility classes to remove these arrows and create clean, customized number inputs. We'll also look at how to apply this styling globally for larger projects and how to add accessible custom controls.</p>
<h2 id="inline-approach">Inline Approach</h2>
<p>Let's start with an example that uses inline Tailwind classes:</p>
<pre><code class="hljsjavascript">&lt;form <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"max-w-md mx-auto mt-8 p-6 bg-white rounded-lg shadow-md"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mb-4"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"quantity"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700 mb-2"</span>&gt;</span>Quantity<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"number"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"quantity"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"quantity"</span> 
      <span class="hljs-attr">class</span>=<span class="hljs-string">"block w-full px-3 py-2 bg-white border border-gray-300 rounded-md text-sm shadow-sm
      focus:outline-none focus:border-sky-500 focus:ring-1 focus:ring-sky-500
      [appearance:textfield] [&amp;::-webkit-outer-spin-button]:appearance-none [&amp;::-webkit-inner-spin-button]:appearance-none"</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mb-4"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"price"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700 mb-2"</span>&gt;</span>Price<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"number"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"price"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"price"</span> <span class="hljs-attr">step</span>=<span class="hljs-string">"0.01"</span>
      <span class="hljs-attr">class</span>=<span class="hljs-string">"block w-full px-3 py-2 bg-white border border-gray-300 rounded-md text-sm shadow-sm
      focus:outline-none focus:border-sky-500 focus:ring-1 focus:ring-sky-500
      [appearance:textfield] [&amp;::-webkit-outer-spin-button]:appearance-none [&amp;::-webkit-inner-spin-button]:appearance-none"</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-full bg-blue-500 text-white py-2 px-4 rounded-md hover:bg-blue-600 transition-colors"</span>&gt;</span>
    Submit
  <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</span></code></pre>
<p>The key classes for removing the arrows are:</p>
<ul>
<li><code>[appearance:textfield]</code>: Removes default styling in Firefox.</li>
<li><code>[&amp;::-webkit-outer-spin-button]:appearance-none</code>: Removes outer spin button in WebKit browsers.</li>
<li><code>[&amp;::-webkit-inner-spin-button]:appearance-none</code>: Removes inner spin button in WebKit browsers.</li>
</ul>
<p><img src="https://imgur.com/UM5WkOx.png" alt="Number input without arrows"></p>
<h2 id="global-approach">Global Approach</h2>
<p>For larger projects, you might want to apply this styling to all number inputs. You can do this by adding styles to your global CSS file:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<ol>
<li>
<p>Open your <code>global.css</code> file (or equivalent, like <code>app.css</code> or <code>styles.css</code>) depending on your framework and setup.</p>
</li>
<li>
<p>Add the following CSS:</p>
</li>
</ol>
<pre><code class="hljscss"><span class="hljs-comment">/* In your global.css file */</span>
<span class="hljs-keyword">@layer</span> utilities {
  <span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"number"</span>]</span><span class="hljs-selector-pseudo">::-webkit-inner-spin-button</span>,
  <span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"number"</span>]</span><span class="hljs-selector-pseudo">::-webkit-outer-spin-button</span> {
    <span class="hljs-attribute">-webkit-appearance</span>: none;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  }

  <span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"number"</span>]</span> {
    <span class="hljs-attribute">-moz-appearance</span>: textfield;
  }
}
</code></pre>
<ol start="3">
<li>Ensure this CSS file is imported in your main Tailwind CSS file or included in your HTML.</li>
</ol>
<p>After adding these global styles, you can simplify your HTML:</p>
<pre><code class="hljsjavascript">&lt;form <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"max-w-md mx-auto mt-8 p-6 bg-white rounded-lg shadow-md"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mb-4"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"quantity"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700 mb-2"</span>&gt;</span>Quantity<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"number"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"quantity"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"quantity"</span> 
      <span class="hljs-attr">class</span>=<span class="hljs-string">"block w-full px-3 py-2 bg-white border border-gray-300 rounded-md text-sm shadow-sm
      focus:outline-none focus:border-sky-500 focus:ring-1 focus:ring-sky-500"</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mb-4"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"price"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700 mb-2"</span>&gt;</span>Price<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"number"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"price"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"price"</span> <span class="hljs-attr">step</span>=<span class="hljs-string">"0.01"</span>
      <span class="hljs-attr">class</span>=<span class="hljs-string">"block w-full px-3 py-2 bg-white border border-gray-300 rounded-md text-sm shadow-sm
      focus:outline-none focus:border-sky-500 focus:ring-1 focus:ring-sky-500"</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-full bg-blue-500 text-white py-2 px-4 rounded-md hover:bg-blue-600 transition-colors"</span>&gt;</span>
    Submit
  <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</span></code></pre>
<p>Notice that we've removed the arrow-removing classes from individual inputs, as they're now handled by the global CSS.</p>
<h2 id="adding-custom-arrows">Adding Custom Arrows</h2>
<p>While removing default arrows improves design consistency, it's crucial to maintain accessibility. Here's how to create custom, accessible increment/decrement buttons that match our form's design:</p>
<pre><code class="hljsjavascript">&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"mb-4"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"quantity"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700 mb-2"</span>&gt;</span>Quantity<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"relative flex items-center"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"number"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"quantity"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"quantity"</span> 
      <span class="hljs-attr">class</span>=<span class="hljs-string">"block w-full px-3 py-2 bg-white border border-gray-300 rounded-md text-sm shadow-sm
      focus:outline-none focus:border-sky-500 focus:ring-1 focus:ring-sky-500
      [appearance:textfield] [&amp;::-webkit-outer-spin-button]:appearance-none [&amp;::-webkit-inner-spin-button]:appearance-none"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"absolute right-0 flex items-center"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> 
              <span class="hljs-attr">class</span>=<span class="hljs-string">"px-2 h-full border-l border-gray-300 text-gray-500 hover:text-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2"</span>
              <span class="hljs-attr">onclick</span>=<span class="hljs-string">"document.getElementById('quantity').stepUp()"</span>
              <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Increase quantity"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span> <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"1.5"</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-4 h-4"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">stroke-linecap</span>=<span class="hljs-string">"round"</span> <span class="hljs-attr">stroke-linejoin</span>=<span class="hljs-string">"round"</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M4.5 15.75l7.5-7.5 7.5 7.5"</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> 
              <span class="hljs-attr">class</span>=<span class="hljs-string">"px-2 h-full border-l border-gray-300 text-gray-500 hover:text-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2"</span>
              <span class="hljs-attr">onclick</span>=<span class="hljs-string">"document.getElementById('quantity').stepDown()"</span>
              <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Decrease quantity"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span> <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"1.5"</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-4 h-4"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">stroke-linecap</span>=<span class="hljs-string">"round"</span> <span class="hljs-attr">stroke-linejoin</span>=<span class="hljs-string">"round"</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M19.5 8.25l-7.5 7.5-7.5-7.5"</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>Key accessibility and styling improvements:</p>
<ol>
<li>We've added <code>aria-label</code> attributes to the buttons, providing context for screen readers.</li>
<li>The <code>focus</code> states now include a visible ring (<code>focus:ring-2 focus:ring-sky-500 focus:ring-offset-2</code>) to improve keyboard navigation visibility.</li>
<li>The buttons are styled consistently with the input field, maintaining the form's overall design.</li>
</ol>
<h2 id="important-considerations">Important Considerations</h2>
<p>There are a few things that you should consider:</p>
<ol>
<li>
<p>Accessibility: While removing default arrows can improve visual design, it's crucial to maintain accessibility. Always provide alternative methods for incrementing/decrementing values, such as custom buttons with proper ARIA labels.</p>
</li>
<li>
<p>Keyboard Navigation: Ensure that your custom controls are fully accessible via keyboard. This includes proper focus states and the ability to activate buttons using the keyboard.</p>
</li>
<li>
<p>Cross-browser Compatibility: This solution works in modern browsers. Older browsers may require additional CSS or JavaScript.</p>
</li>
</ol>
<h2 id="conclusion">Conclusion</h2>
<p>By implementing these techniques, you can effectively remove the default arrows from number inputs across your project while maintaining accessibility and consistent styling. Remember to always test your forms with various input methods (mouse, keyboard, touch) and assistive technologies to ensure a great user experience for all.</p>
<p>For those looking to improve their Tailwind CSS development process further, check out the <a href="https://devdojo.com/tails">DevDojo Tails page builder</a>, which can help you create amazing designs with ease.</p>
<p>Happy coding!</p>
]]></description>
                                                            <category>css</category>
                                            <category>html</category>
                                            <category>tailwind</category>
                                            <category>webdev</category>
                                            <category>tailwind css</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12213</guid>
                <pubDate>Wed, 10 Jul 2024 06:32:57 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to Make Parent Div Activate Styling of Child Div for Hover and Active States]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-make-parent-div-activate-styling-of-child-div-for-hover-and-active-states</link>
                <description><![CDATA[<p>Hey there developers! 👋</p>
<p>Today, we're diving into a Tailwind CSS trick! We'll explore how to make a parent div control the styling of its child elements on hover and active states. Let's jump right in!</p>
<h2 id="the-problem">The Problem</h2>
<p>You've probably encountered situations where you want an entire component to respond to user interactions, not just individual elements.</p>
<p>For example, you might want a card to change its appearance when hovered, including all its child elements. Tailwind CSS has a elegant solution for this: the <code>group</code> and <code>group-hover</code> utilities.</p>
<h2 id="the-solution-group-and-group-hover">The Solution: Group and Group-Hover</h2>
<p>Tailwind CSS provides a powerful feature called "group hover" that allows us to style child elements based on the state of a parent element. Here's how it works:</p>
<ol>
<li>Add the <code>group</code> class to your parent element.</li>
<li>Use <code>group-hover:</code> prefix on child elements to apply styles when the parent is hovered.</li>
</ol>
<p>Let's see this in action with a cool example:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"group p-6 bg-gray-100 rounded-lg transition-all duration-300 hover:bg-blue-100"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-2xl font-bold text-gray-800 group-hover:text-blue-600"</span>&gt;</span>Awesome Feature<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-2 text-gray-600 group-hover:text-blue-500"</span>&gt;</span>This feature will blow your mind!<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-4 px-4 py-2 bg-blue-500 text-white rounded group-hover:bg-blue-600"</span>&gt;</span>
    Learn More
  <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>In this example, when you hover over the parent <code>div</code>, the heading, paragraph, and button all change color simultaneously. Pretty cool, right?</p>
<h2 id="taking-it-further-group-active">Taking It Further: Group-Active</h2>
<p>But wait, there's more! We can also use the <code>group-active</code> variant to style child elements when the parent is in an active state (e.g., being clicked). Here's an enhanced version of our previous example:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"group p-6 bg-gray-100 rounded-lg transition-all duration-300 hover:bg-blue-100 active:bg-blue-200"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-2xl font-bold text-gray-800 group-hover:text-blue-600 group-active:text-blue-800"</span>&gt;</span>Awesome Feature<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-2 text-gray-600 group-hover:text-blue-500 group-active:text-blue-700"</span>&gt;</span>This feature will blow your mind!<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-4 px-4 py-2 bg-blue-500 text-white rounded group-hover:bg-blue-600 group-active:bg-blue-800"</span>&gt;</span>
    Learn More
  <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Now, when you click on the component, you'll see an additional style change. It's like magic, but it's just the power of Tailwind CSS! 🎩✨</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<h2 id="pro-tip-extending-core-plugins">Pro Tip: Extending Core Plugins</h2>
<p>It's worth noting that not every property supports <code>group-hover</code> or <code>group-active</code> out of the box. In some cases, you might need to extend Tailwind's core plugins. You can do this in your <code>tailwind.config.js</code> file:</p>
<pre><code class="hljsjavascript"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-comment">// ...other config</span>
  <span class="hljs-attr">plugins</span>: [
    plugin(<span class="hljs-function">(<span class="hljs-params">{ addVariant }</span>) =&gt;</span> {
      addVariant(<span class="hljs-string">'group-hover'</span>, <span class="hljs-string">':merge(.group):hover &amp;'</span>)
      addVariant(<span class="hljs-string">'group-active'</span>, <span class="hljs-string">':merge(.group):active &amp;'</span>)
    })
  ],
}
</code></pre>
<p>This will allow you to use <code>group-hover</code> and <code>group-active</code> with any utility in Tailwind CSS.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>And there you have it! You've just learned how to make parent divs control the styling of their child elements for hover and active states using Tailwind CSS.</p>
<p>As a next step, check out DevDojo's Tails page builder at <a href="https://devdojo.com/tails/">https://devdojo.com/tails/</a>. It's a fantastic visual builder that lets you create stunning Tailwind CSS powered pages with ease. Give it a spin and see how it can supercharge your development workflow!</p>
<p>Keep coding, stay curious, and until next time, may your builds be bug-free and your coffee strong! 💻☕️</p>
]]></description>
                                                            <category>css</category>
                                            <category>html</category>
                                            <category>tailwind</category>
                                            <category>webdev</category>
                                            <category>tailwind css</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12210</guid>
                <pubDate>Fri, 05 Jul 2024 08:39:57 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to Access Direct Children of a Div in Tailwind CSS v3]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-access-direct-children-of-a-div-in-tailwind-css-v3</link>
                <description><![CDATA[<p>In this tutorial, we'll explore how to target and style the direct children of a div using Tailwind CSS v3's powerful arbitrary value syntax. This feature allows for more flexible and precise styling, especially when dealing with nested layouts.</p>
<h2 id="the-problem">The Problem</h2>
<p>Consider the following HTML structure:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"section"</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"footer"</span>&gt;</span>footer<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"content"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>sub contents 1<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>              
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>sub contents 2<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
   <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>We want to style only the direct children of the div with the class "section", which are the divs with classes "footer" and "content". In regular CSS, we'd use the child combinator selector like this: <code>div.section &gt; div</code>. But how do we achieve this in Tailwind CSS v3?</p>
<h2 id="the-solution-arbitrary-value-syntax">The Solution: Arbitrary Value Syntax</h2>
<p>Tailwind CSS v3 introduced the arbitrary value syntax, which allows us to use the <code>&amp;</code> character to reference the current selector. This feature provides a powerful way to target direct children. Here's how you can do it:</p>
<h3 id="syntax-class">Syntax: <code>[&amp;&gt;*]:{class}</code></h3>
<ul>
<li><code>&amp;</code> represents the current element</li>
<li><code>&gt;</code> is the child combinator</li>
<li><code>*</code> selects all direct children</li>
<li><code>:{class}</code> applies the specified Tailwind class</li>
</ul>
<p>Let's see this in action with some examples.</p>
<h3 id="example-1-adding-padding-to-all-direct-children">Example 1: Adding padding to all direct children</h3>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"section [&amp;&gt;*]:p-4"</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"footer"</span>&gt;</span>footer<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"content"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>sub contents 1<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>              
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>sub contents 2<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
   <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>In this example, <code>[&amp;&gt;*]:p-4</code> adds padding of 1rem (p-4) to all direct children of the "section" div.</p>
<h3 id="example-2-targeting-specific-child-elements">Example 2: Targeting specific child elements</h3>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"section [&amp;&gt;div]:bg-gray-100 [&amp;&gt;.footer]:text-red-500"</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"footer"</span>&gt;</span>footer<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"content"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>sub contents 1<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>              
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>sub contents 2<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
   <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Here, we're applying different styles to different direct children:</p>
<ul>
<li><code>[&amp;&gt;div]:bg-gray-100</code> adds a light gray background to all direct div children.</li>
<li><code>[&amp;&gt;.footer]:text-red-500</code> makes the text red only for the direct child with the "footer" class.</li>
</ul>
<h3 id="example-3-combining-multiple-styles">Example 3: Combining multiple styles</h3>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"section [&amp;&gt;*]:p-4 [&amp;&gt;*]:mb-2 [&amp;&gt;.content]:bg-blue-100"</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"footer"</span>&gt;</span>footer<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"content"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>sub contents 1<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>              
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>sub contents 2<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
   <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>This example combines multiple arbitrary value selectors:</p>
<ul>
<li><code>[&amp;&gt;*]:p-4</code> adds padding to all direct children.</li>
<li><code>[&amp;&gt;*]:mb-2</code> adds margin-bottom to all direct children.</li>
<li><code>[&amp;&gt;.content]:bg-blue-100</code> applies a light blue background only to the "content" div.</li>
</ul>
<h2 id="practical-example">Practical Example</h2>
<p>Let's create a footer with multiple columns, social media links, and a newsletter signup form. We'll use the arbitrary value syntax to style the direct children of our footer container.</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">footer</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-gray-800 text-white py-12"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container mx-auto px-4"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"[&amp;&gt;div]:mb-8 [&amp;&gt;div:last-child]:mb-0 md:[&amp;&gt;div]:mb-0 md:flex md:justify-between"</span>&gt;</span>
      <span class="hljs-comment">&lt;!-- Company Info --&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"md:w-1/4"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-2xl font-bold mb-4"</span>&gt;</span>TechCorp<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"[&amp;&gt;a]:text-blue-400 [&amp;&gt;a]:hover:underline"</span>&gt;</span>
          123 Tech Street, San Francisco, CA 94122<span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"mailto:info@techcorp.com"</span>&gt;</span>info@techcorp.com<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"tel:+14155551234"</span>&gt;</span>(415) 555-1234<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

      <span class="hljs-comment">&lt;!-- Quick Links --&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"md:w-1/4"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-lg font-semibold mb-4"</span>&gt;</span>Quick Links<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"[&amp;&gt;li]:mb-2 [&amp;&gt;li&gt;a]:text-gray-300 [&amp;&gt;li&gt;a]:hover:text-white [&amp;&gt;li&gt;a]:transition-colors"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>About Us<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>Services<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>Blog<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>Contact<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

      <span class="hljs-comment">&lt;!-- Social Media --&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"md:w-1/4"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-lg font-semibold mb-4"</span>&gt;</span>Connect With Us<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex [&amp;&gt;a]:mr-4 [&amp;&gt;a]:text-2xl [&amp;&gt;a]:text-gray-300 [&amp;&gt;a]:hover:text-white [&amp;&gt;a]:transition-colors"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Facebook"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-facebook"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Twitter"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-twitter"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"LinkedIn"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-linkedin"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Instagram"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-instagram"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

      <span class="hljs-comment">&lt;!-- Newsletter Signup --&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"md:w-1/4"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-lg font-semibold mb-4"</span>&gt;</span>Subscribe to Our Newsletter<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"[&amp;&gt;*]:mb-2 [&amp;&gt;*:last-child]:mb-0"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span> 
            <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> 
            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter your email"</span> 
            <span class="hljs-attr">class</span>=<span class="hljs-string">"w-full px-4 py-2 bg-gray-700 text-white rounded focus:outline-none focus:ring-2 focus:ring-blue-500"</span>
          &gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
            <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> 
            <span class="hljs-attr">class</span>=<span class="hljs-string">"w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded transition-colors"</span>
          &gt;</span>
            Subscribe
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
</code></pre>
<p>![https://imgur.com/B6XHhRx.png]</p>
<p>In this example, we've used several instances of the arbitrary value syntax to style our footer:</p>
<ol>
<li>
<p>For the main container:</p>
<ul>
<li><code>[&amp;&gt;div]:mb-8</code>: Adds margin-bottom to all direct child divs.</li>
<li><code>[&amp;&gt;div:last-child]:mb-0</code>: Removes margin-bottom from the last child div.</li>
<li><code>md:[&amp;&gt;div]:mb-0</code>: Removes margin-bottom from all child divs on medium screens and up.</li>
</ul>
</li>
<li>
<p>For the company info section:</p>
<ul>
<li><code>[&amp;&gt;a]:text-blue-400</code>: Makes all direct child links blue.</li>
<li><code>[&amp;&gt;a]:hover:underline</code>: Underlines links on hover.</li>
</ul>
</li>
<li>
<p>For the quick links section:</p>
<ul>
<li><code>[&amp;&gt;li]:mb-2</code>: Adds margin-bottom to all list items.</li>
<li><code>[&amp;&gt;li&gt;a]:text-gray-300</code>: Sets the text color of links within list items.</li>
<li><code>[&amp;&gt;li&gt;a]:hover:text-white</code>: Changes link color on hover.</li>
<li><code>[&amp;&gt;li&gt;a]:transition-colors</code>: Adds a smooth transition effect for color changes.</li>
</ul>
</li>
<li>
<p>For the social media section:</p>
<ul>
<li><code>[&amp;&gt;a]:mr-4</code>: Adds margin-right to all links.</li>
<li><code>[&amp;&gt;a]:text-2xl</code>: Sets the font size for social icons.</li>
<li><code>[&amp;&gt;a]:text-gray-300</code>: Sets the initial color for social icons.</li>
<li><code>[&amp;&gt;a]:hover:text-white</code>: Changes icon color on hover.</li>
</ul>
</li>
<li>
<p>For the newsletter form:</p>
<ul>
<li><code>[&amp;&gt;*]:mb-2</code>: Adds margin-bottom to all direct children of the form.</li>
<li><code>[&amp;&gt;*:last-child]:mb-0</code>: Removes margin-bottom from the last child of the form.</li>
</ul>
</li>
</ol>
<h2 id="conclusion">Conclusion</h2>
<p>Tailwind CSS v3's arbitrary value syntax provides a powerful and flexible way to target and style direct children of an element. This approach allows you to:</p>
<ol>
<li>Apply styles to all direct children using <code>[&amp;&gt;*]:{class}</code>.</li>
<li>Target specific direct children using class or element selectors like <code>[&amp;&gt;.classname]:{class}</code> or <code>[&amp;&gt;element]:{class}</code>.</li>
<li>Combine multiple selectors for more complex styling needs.</li>
</ol>
<p>If you're looking to improve your Tailwind CSS development process even further, check out <a href="https://devdojo.com/tails">Tails by DevDojo</a>. This powerful page builder allows you to visually create stunning, responsive Tailwind CSS websites with drag-and-drop ease, making it an excellent tool for both beginners and experienced developers alike.</p>
<p>Happy coding!</p>
]]></description>
                                                            <category>css</category>
                                            <category>tailwindcss</category>
                                            <category>tailwind</category>
                                            <category>webdev</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12207</guid>
                <pubDate>Wed, 03 Jul 2024 04:43:23 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to Vertically Align Content with Tailwind CSS Across a Full-Screen Div]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-vertically-align-content-with-tailwind-css-across-a-full-screen-div</link>
                <description><![CDATA[<p>Vertical alignment can often be a challenge in web design, but with Tailwind CSS, you can easily align elements in the center of the screen.</p>
<p>This quick guide will walk you through the steps to vertically align content within a full-screen div using Tailwind CSS, complete with nicely styled examples.</p>
<p></p><div style="width:100%;height:0;padding-bottom:56%;position:relative;display:block"><iframe src="https://giphy.com/embed/13FrpeVH09Zrb2" width="100%" height="100%" style="position:absolute" frameborder="0" class="giphy-embed" allowfullscreen></iframe></div>
<h2 id="step-1-setting-up-tailwind-css">Step 1: Setting Up Tailwind CSS</h2>
<p>First, make sure you have Tailwind CSS set up in your project. If you're starting from scratch, you can use the following CDN link in your HTML file:</p>
<pre><code class="hljsxml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Vertical Alignment with Tailwind CSS<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Your content will go here --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>If you're using a build tool like Webpack or a framework like Next.js, refer to the <a href="https://tailwindcss.com/docs/installation">Tailwind CSS installation guide</a> for the appropriate setup.</p>
<h2 id="step-2-creating-the-full-screen-div">Step 2: Creating the Full-Screen Div</h2>
<p>To create a full-screen div, we'll use Tailwind's utility classes. We'll start by creating a div that spans the full viewport height and width. Here's a simple example:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"min-h-screen flex items-center justify-center bg-gray-100"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Content goes here --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<ul>
<li><code>min-h-screen</code>: This class sets the minimum height of the div to the full height of the viewport.</li>
<li><code>flex</code>: This makes the div a flex container.</li>
<li><code>items-center</code>: This vertically centers the content inside the flex container.</li>
<li><code>justify-center</code>: This horizontally centers the content inside the flex container.</li>
<li><code>bg-gray-100</code>: This adds a light gray background color to the div.</li>
</ul>
<h2 id="step-3-adding-content">Step 3: Adding Content</h2>
<p>Now, let's add some content inside our full-screen div. We'll use a simple card component as our example:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"min-h-screen flex items-center justify-center bg-gray-100"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-white p-8 rounded-lg shadow-lg"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-2xl font-bold mb-4"</span>&gt;</span>Vertically Aligned Content<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-700"</span>&gt;</span>This content is centered both vertically and horizontally using Tailwind CSS.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<ul>
<li><code>bg-white</code>: This sets the background color of the card to white.</li>
<li><code>p-8</code>: This adds padding to the card.</li>
<li><code>rounded-lg</code>: This rounds the corners of the card.</li>
<li><code>shadow-lg</code>: This adds a large shadow to the card.</li>
<li><code>text-2xl</code>: This sets the font size of the heading to 2xl.</li>
<li><code>font-bold</code>: This makes the heading bold.</li>
<li><code>mb-4</code>: This adds a bottom margin to the heading.</li>
<li><code>text-gray-700</code>: This sets the color of the paragraph text to a dark gray.</li>
</ul>
<h2 id="step-4-styling-the-content">Step 4: Styling the Content</h2>
<p>To make our example more visually appealing, we can add some additional styling. Let's enhance the card with a more polished look:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"min-h-screen flex items-center justify-center bg-gradient-to-r from-blue-500 to-purple-600"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-white p-8 rounded-lg shadow-2xl transform hover:scale-105 transition-transform duration-300"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-3xl font-extrabold mb-6 text-transparent bg-clip-text bg-gradient-to-r from-green-400 to-blue-500"</span>&gt;</span>
            Vertically Aligned Content
        <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-800 text-lg"</span>&gt;</span>
            This content is centered both vertically and horizontally using Tailwind CSS.
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<ul>
<li><code>bg-gradient-to-r from-blue-500 to-purple-600</code>: This creates a background gradient for the full-screen div.</li>
<li><code>shadow-2xl</code>: This adds a larger shadow to the card.</li>
<li><code>transform hover:scale-105 transition-transform duration-300</code>: This adds a scaling effect when the card is hovered over, with a smooth transition.</li>
<li><code>text-3xl</code>: This sets the font size of the heading to 3xl.</li>
<li><code>font-extrabold</code>: This makes the heading extra bold.</li>
<li><code>text-transparent bg-clip-text bg-gradient-to-r from-green-400 to-blue-500</code>: This creates a gradient text effect for the heading.</li>
<li><code>text-lg</code>: This sets the font size of the paragraph text to large.</li>
</ul>
<p><img src="https://imgur.com/WVmQnRv.png" alt=""></p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<h2 id="conclusion">Conclusion</h2>
<p>By using Tailwind CSS's utility classes, you can easily vertically align content within a full-screen div. The flexbox utilities provided by Tailwind make it simple to center content both vertically and horizontally with just a few classes.</p>
<p>For even more styling options and to create beautiful designs effortlessly, check out the <a href="https://devdojo.com/tails">DevDojo Tails Tailwind CSS builder</a>. It's a fantastic tool to help you with your workflow and create stunning designs with Tailwind CSS.</p>
]]></description>
                                                            <category>css</category>
                                            <category>tailwind css</category>
                                            <category>web dev</category>
                                            <category>design</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12198</guid>
                <pubDate>Sun, 30 Jun 2024 13:19:57 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to Get the Directory Where a Bash Script is Located]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-get-the-directory-where-a-bash-script-is-located</link>
                <description><![CDATA[<p>Hello everyone!</p>
<p>When you're working with Bash scripts, it's often useful to know the directory where the script itself resides. This can be essential for referencing relative paths, ensuring the script works correctly regardless of where it is executed from. Let’s dive into how you can achieve this.</p>
<h3 id="method-1-using-dirname-and-0">Method 1: Using <code>dirname</code> and <code>$0</code></h3>
<p>The simplest way to get the directory of the script is to use the <code>dirname</code> command in combination with <code>$0</code>. Here's a step-by-step explanation:</p>
<ol>
<li><strong><code>$0</code></strong>: This special variable contains the path used to invoke the script. It might be a relative path, an absolute path, or just the script name.</li>
<li><strong><code>dirname</code></strong>: This command removes the last component from a path, effectively giving you the directory part.</li>
</ol>
<p>Here’s a small snippet to demonstrate this:</p>
<pre><code class="hljsperl"><span class="hljs-comment">#!/bin/bash</span>

<span class="hljs-comment"># Get the directory of the script</span>
SCRIPT_DIR=$(dirname <span class="hljs-string">"$0"</span>)

echo <span class="hljs-string">"The script is located in: $SCRIPT_DIR"</span>
</code></pre>
<h3 id="method-2-resolving-the-full-path">Method 2: Resolving the Full Path</h3>
<p>If you need the absolute path, especially if the script is run from a relative path, you can combine <code>dirname</code> with <code>readlink</code>:</p>
<pre><code class="hljspowershell"><span class="hljs-comment">#!/bin/bash</span>

<span class="hljs-comment"># Resolve the full path of the script</span>
SCRIPT_DIR=<span class="hljs-variable">$</span>(dirname <span class="hljs-string">"<span class="hljs-variable">$</span>(readlink -f "</span><span class="hljs-variable">$0</span><span class="hljs-string">")"</span>)

echo <span class="hljs-string">"The script is located in: <span class="hljs-variable">$SCRIPT_DIR</span>"</span>
</code></pre>
<h3 id="breaking-down-the-command">Breaking Down the Command</h3>
<ul>
<li><strong><code>readlink -f "$0"</code></strong>: This command follows all symlinks and returns the absolute path of the script.</li>
<li><strong><code>dirname "$(readlink -f "$0")"</code></strong>: This gives the absolute directory path of the script.</li>
</ul>
<h3 id="method-3-handling-edge-cases">Method 3: Handling Edge Cases</h3>
<p>For more complex scenarios, such as when the script is sourced or if it's part of a symlink chain, you might need additional logic to ensure you always get the correct directory.</p>
<p>Here’s an advanced version that handles more edge cases:</p>
<pre><code class="hljsperl"><span class="hljs-comment">#!/bin/bash</span>

<span class="hljs-comment"># Function to resolve the script path</span>
get_script_dir() {
    <span class="hljs-keyword">local</span> SOURCE=$0
    <span class="hljs-keyword">while</span> [ -h <span class="hljs-string">"$SOURCE"</span> ]; <span class="hljs-keyword">do</span> <span class="hljs-comment"># Resolve $SOURCE until the file is no longer a symlink</span>
        DIR=$(cd -P <span class="hljs-string">"$(dirname "</span>$SOURCE<span class="hljs-string">")"</span> &amp;&amp; pwd)
        SOURCE=$(<span class="hljs-keyword">readlink</span> <span class="hljs-string">"$SOURCE"</span>)
        [[ $SOURCE != <span class="hljs-regexp">/* ]] &amp;&amp; SOURCE=$DIR/</span>$SOURCE <span class="hljs-comment"># If $SOURCE was a relative symlink, resolve it relative to the symlink base directory</span>
    done
    DIR=$(cd -P <span class="hljs-string">"$(dirname "</span>$SOURCE<span class="hljs-string">")"</span> &amp;&amp; pwd)
    echo <span class="hljs-string">"$DIR"</span>
}

<span class="hljs-comment"># Get the directory</span>
SCRIPT_DIR=$(get_script_dir)

echo <span class="hljs-string">"The script is located in: $SCRIPT_DIR"</span>
</code></pre>
<h3 id="conclusion">Conclusion</h3>
<p>Getting the directory of the Bash script from within the script itself is a common requirement and can be easily achieved using <code>dirname</code> and <code>$0</code>. For more robust solutions, combining these with <code>readlink</code> can ensure you handle absolute paths and symlinks correctly. By incorporating these methods into your scripts, you can make them more portable and easier to manage.</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<p>If you’re interested in learning more about Bash scripting, be sure to check out my <a href="https://github.com/bobbyiliev/introduction-to-bash-scripting">free eBook on Bash scripting</a>. It's packed with useful information and examples to help you become a Bash scripting pro!</p>
<p>Happy scripting!</p>
]]></description>
                                                            <category>bash</category>
                                            <category>linux</category>
                                            <category>devops</category>
                                            <category>scripting</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/12181</guid>
                <pubDate>Wed, 19 Jun 2024 06:49:13 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Running Laravel Wave with Sail: A Step-by-Step Guide]]></title>
                <link>https://devdojo.com/bobbyiliev/running-laravel-wave-with-sail-a-step-by-step-guide</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>This tutorial will guide you through the process of running Laravel Wave using Laravel Sail, a light-weight command-line interface for managing Dockerized Laravel applications. This tutorial is perfect for those who prefer a Docker-based environment for their Laravel applications. Let's dive in!</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>Before we begin, ensure you have:</p>
<ul>
<li>PHP 8.1</li>
<li>Composer installed on your system</li>
</ul>
<p>These are essential for running any Laravel application.</p>
<h2 id="getting-started-with-laravel-wave">Getting Started with Laravel Wave</h2>
<p>First things first, let's get the Laravel Wave repository. Fork and clone the repository from GitHub:</p>
<pre><code class="hljsruby">git clone git@github.<span class="hljs-symbol">com:</span>thedevdojo/wave.git
</code></pre>
<blockquote>
<p>Note: if you've cloned the repository change <code>thedevdojo</code> with your GitHub username</p>
</blockquote>
<p>Cloning the repository provides you with the necessary files to get started.</p>
<h2 id="setting-up-laravel-sail">Setting Up Laravel Sail</h2>
<p>Laravel Sail is a simple solution for running your Laravel project using Docker. Follow these steps to set it up:</p>
<h3 id="install-dependencies">Install Dependencies</h3>
<p>Navigate to your project directory and install the Composer dependencies:</p>
<pre><code class="hljs">composer install
</code></pre>
<p>This command pulls in all the necessary PHP packages required by Laravel Wave.</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<h3 id="environment-configuration">Environment Configuration</h3>
<p>Next, you need to set up your environment file. Copy the <code>.env.example</code> file to create your <code>.env</code> file:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">cp</span> <span class="hljs-selector-class">.env</span><span class="hljs-selector-class">.example</span> <span class="hljs-selector-class">.env</span>
</code></pre>
<p>After copying the file, update the database details in your <code>.env</code> file to match the following:</p>
<pre><code class="hljs">DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=wave
DB_USERNAME=sail
DB_PASSWORD=password
</code></pre>
<p>These settings configure your application to connect to the MySQL database provided by Sail.</p>
<h3 id="start-sail">Start Sail</h3>
<p>Now, it's time to start your Dockerized environment with Sail:</p>
<pre><code class="hljs">bash ./vendor/bin/sail up -d
</code></pre>
<p>This command starts all the necessary Docker containers in the background.</p>
<h3 id="database-migrations-and-seeding">Database Migrations and Seeding</h3>
<p>With your environment up and running, proceed to set up your database:</p>
<pre><code class="hljsperl">./vendor/bin/sail artisan migrate 
./vendor/bin/sail artisan db:seed
./vendor/bin/sail artisan storage:<span class="hljs-keyword">link</span>
</code></pre>
<p>These commands create the necessary database tables, seed them with data, and set up storage links.</p>
<h2 id="accessing-your-application">Accessing Your Application</h2>
<p>Finally, after completing the setup, you can access your Laravel Wave application by visiting <code>http://localhost</code> in your browser.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Setting up Laravel Wave with Sail offers a seamless and straightforward way to manage your local Laravel Wave dev app in a Docker environment. This approach not only simplifies the development process but also ensures consistency across different development environments.</p>
<p>For production, I could suggest using DigitalOcean. If you wish, you can use my affiliate code to get <a href="https://m.do.co/c/2a9bba940f39">free $200 DigitalOcean credit</a> to spin up your own servers!</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>Happy coding, and as always, feel free to reach out if you have any questions or need further assistance!</p>
]]></description>
                                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/11851</guid>
                <pubDate>Tue, 26 Dec 2023 05:59:04 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[Introduction to Linux eBook: A Comprehensive Guide for Beginners]]></title>
                <link>https://devdojo.com/bobbyiliev/introduction-to-linux-ebook-a-comprehensive-guide-for-beginners</link>
                <description><![CDATA[<h3 id="introduction">Introduction</h3>
<p>Hello to all passionate learners, tech enthusiasts, and budding system administrators!</p>
<p>I'm thrilled to announce the release of my latest eBook: <strong>"Introduction to Linux"</strong>. After months of dedicated research, writing, and refining, I've put together a guide that aims to help beginners and provide a solid foundation for those looking to gain a deeper understanding.</p>
<h4 id="why-linux"><strong>Why Linux?</strong></h4>
<p>Linux is a powerful operating system that has become a cornerstone of modern computing. It's a versatile platform that can be used for everything from running web servers to powering mobile devices. Linux is also open-source, meaning anyone can contribute to its development and use it free of charge.</p>
<p>Gaining proficiency in Linux not only opens the door to understanding this powerful OS but also paves the way for numerous career opportunities.</p>
<h4 id="what-can-you-expect-from-the-ebook"><strong>What Can You Expect from the eBook?</strong></h4>
<ul>
<li><strong>Foundational Concepts</strong>: Delve into the history and philosophy of Linux, its various distributions, and its place in the modern tech ecosystem.</li>
<li><strong>Hands-on Learning</strong>: Engage with practical exercises and real-world examples that guide you through navigating the Linux filesystem, working with the command line, and much more.</li>
<li><strong>Deep Dives</strong>: Explore topics like process management, user permissions, networking basics, and even bash scripting. Perfect for those who wish to transition from beginners to adept users.</li>
</ul>
<h4 id="who-is-this-book-for"><strong>Who is this Book For?</strong></h4>
<p>Whether you're an absolute beginner curious about Linux, a developer looking to hone your system administration skills, or an IT professional aiming to broaden your knowledge base, "Introduction to Linux" is for you.</p>
<h4 id="a-glimpse-into-my-journey"><strong>A Glimpse into My Journey</strong></h4>
<p>Writing this eBook was not just about compiling information; it was a journey of revisiting the foundational concepts, engaging with the vast Linux community, and reflecting on my experiences as a Linux system administrator. I've always believed in the power of shared knowledge, and this book is my humble contribution to the ever-growing repository of Linux wisdom.</p>
<h4 id="ready-to-get-started"><strong>Ready to Get Started?</strong></h4>
<p>I invite you to dive into the world of Linux with this comprehensive guide. It's designed to be both a learning tool and a reference, serving you well as you progress in your journey.</p>
<blockquote>
<p><a href="https://leanpub.com/introduction-to-linux">Introduction to Linux eBook</a></p>
</blockquote>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<p>Happy learning!</p>
]]></description>
                                                            <category>.dev</category>
                                            <category>linux</category>
                                            <category>web</category>
                                            <category>servers</category>
                                            <category>devops</category>
                                            <category>ebook</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/11350</guid>
                <pubDate>Sun, 01 Oct 2023 09:55:59 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Introduction to Terraform: From Zero to Hero eBook]]></title>
                <link>https://devdojo.com/bobbyiliev/introduction-to-terraform-from-zero-to-hero-ebook</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Hey folks, I'm super stoked to announce my brand-new eBook: "<strong>Introduction to Terraform: From Zero to Hero</strong>". This project is something I'm genuinely excited about, born from countless late-night coding sessions and my growing fascination with Infrastructure as Code. I've come to realize that Terraform is an absolute game-changer in the DevOps world, and I can't wait to share everything I've learned with you!</p>
<h2 id="table-of-contents">Table of Contents</h2>
<p>A sneak peek into what's inside:</p>
<ol>
<li>Introduction to Infrastructure as Code (IaC)</li>
<li>Introduction to Terraform</li>
<li>Installation and Setup</li>
<li>Terraform Basics</li>
<li>Terraform Configuration Language (HCL)</li>
<li>Terraform State</li>
<li>Modules in Terraform</li>
<li>Terraform Cloud</li>
<li>Best Practices</li>
<li>Intermediate Terraform Language Features</li>
<li>Importing Existing Infrastructure into Terraform</li>
<li>Advanced State Management</li>
<li>Terraform with Multiple Providers</li>
<li>Terraform for Serverless Architecture</li>
<li>Terraform for Container Orchestration</li>
<li>Terraform with Continuous Integration/Continuous Deployment (CI/CD)</li>
<li>Security and Compliance with Terraform</li>
<li>Managing Secrets with Vault</li>
<li>Troubleshooting and Debugging Terraform</li>
<li>Terraform with Ansible</li>
<li>Advanced Topics and Conclusion</li>
</ol>
<h2 id="why-from-zero-to-hero">Why "From Zero to Hero"?</h2>
<p>The philosophy behind this title is simple. We all start our learning journeys from square one, with a spark of curiosity and an urge to get better at something new. Consider this book your handy roadmap that'll guide you from being a newbie in the world of Infrastructure as Code to really understanding the ins and outs of Terraform. This guide is loaded with in-depth chapters, real-world examples, and hands-on exercises designed to help you in the awesome world of Terraform. By the end of it, I promise you'll be bossing around your own projects like a champ!</p>
<h2 id="who-is-it-for">Who is it for?</h2>
<p>Whether you're a beginner just starting out in the DevOps world or a professional looking to expand your skill set, this book has something for everyone. It's written in a way so that both newcomers and those seeking advanced knowledge could benefit from.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In a rapidly evolving tech world, staying updated and continuously learning is the key. "Introduction to Terraform: From Zero to Hero" isn't just a book; it's your next step in the DevOps world. So, don't hesitate and get your copy today from <a href="https://leanpub.com/introduction-to-terraform">Leanpub</a>. Let's start this exciting adventure together!</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>I genuinely hope you find value in every page of this book.</p>
<p>Until next time, happy learning and terraforming!</p>
<div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>]]></description>
                                                            <category>terraform</category>
                                            <category>devops</category>
                                            <category>ebook</category>
                                            <category>terraform ebook</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/11158</guid>
                <pubDate>Mon, 24 Jul 2023 08:01:49 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to use DigitalOcean Spaces with Laravel Voyager?]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-use-digitalocean-spaces-with-laravel-voyager</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>DigitalOcean Spaces is an object storage service that allows you to store and serve large amounts of data. It is a reliable and flexible solution for developers, especially when integrated with Laravel Voyager - a Laravel package that provides an admin interface with BREAD (Browse, Read, Edit, Add, Delete) operations, media manager, menu builder, and much more.</p>
<p>This tutorial will guide you step-by-step on how to integrate DigitalOcean Spaces with Laravel Voyager.</p>
<p>Before we begin, ensure you have a working Laravel application with Voyager installed. We're also assuming that you have already created your DigitalOcean Space and have the necessary credentials: <code>Spaces Key</code>, <code>Spaces Secret</code>, <code>Spaces region</code>, <code>Spaces Bucket</code> and <code>Spaces endpoint</code>.</p>
<h2 id="laravel-filesystem">Laravel Filesystem</h2>
<p>Laravel provides a clean, simple API over the popular Flysystem PHP package by Frank de Jonge. The Laravel Flysystem integration provides simple to use drivers for working with local filesystems, Amazon S3, and even FTP servers. To utilize this feature for our purpose, we need to install a specific package.</p>
<h2 id="step-1-installing-the-flysystem-aws-s3-v3-package">Step 1: Installing the <code>flysystem-aws-s3-v3</code> Package</h2>
<p>The DigitalOcean Spaces is compatible with the Amazon S3 API. So, we will use the <code>flysystem-aws-s3-v3</code> package to interact with the DigitalOcean Spaces.</p>
<p>Open your terminal and navigate to the root directory of your Laravel project. Now, run the following command:</p>
<pre><code class="hljsjavascript">composer <span class="hljs-built_in">require</span> league/flysystem-aws-s3-v3
</code></pre>
<p>This command will install the <code>flysystem-aws-s3-v3</code> package which will allow us to interact with the DigitalOcean Spaces.</p>
<h2 id="step-2-configuring-the-file-system">Step 2: Configuring the File System</h2>
<p>In the root directory of your Laravel application, open the <code>config/filesystems.php</code> file. Here, we will add a new disk definition for 'spaces':</p>
<pre><code class="hljsphp"><span class="hljs-string">'spaces'</span> =&gt; [
    <span class="hljs-string">'driver'</span> =&gt; <span class="hljs-string">'s3'</span>,
    <span class="hljs-string">'key'</span> =&gt; env(<span class="hljs-string">'DO_SPACES_KEY'</span>),
    <span class="hljs-string">'secret'</span> =&gt; env(<span class="hljs-string">'DO_SPACES_SECRET'</span>),
    <span class="hljs-string">'region'</span> =&gt; env(<span class="hljs-string">'DO_SPACES_REGION'</span>),
    <span class="hljs-string">'bucket'</span> =&gt; env(<span class="hljs-string">'DO_SPACES_BUCKET'</span>),
    <span class="hljs-string">'endpoint'</span> =&gt; env(<span class="hljs-string">'DO_SPACES_ENDPOINT'</span>),
    <span class="hljs-string">'visibility'</span> =&gt; <span class="hljs-string">'public'</span>,
],
</code></pre>
<p>The <code>'visibility' =&gt; 'public'</code> setting is needed to ensure that the images uploaded via the media manager are visible to the public.</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<p>Next, open the <code>.env</code> file and add the following lines:</p>
<pre><code class="hljspowershell">DO_SPACES_KEY=your_spaces_key           <span class="hljs-comment"># Example: 1234567890ABCDEF</span>
DO_SPACES_SECRET=your_spaces_secret     <span class="hljs-comment"># Example: 1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF</span>
DO_SPACES_REGION=your_spaces_region     <span class="hljs-comment"># Example: nyc3</span>
DO_SPACES_BUCKET=your_spaces_bucket     <span class="hljs-comment"># Example: my-bucket</span>
DO_SPACES_ENDPOINT=your_spaces_endpoint <span class="hljs-comment"># Example: https://nyc3.digitaloceanspaces.com</span>
</code></pre>
<p>Note that we didn't add the bucket name to the endpoint. This is because the <code>flysystem-aws-s3-v3</code> package will automatically append the bucket name to the endpoint.</p>
<h2 id="step-3-configuring-voyager">Step 3: Configuring Voyager</h2>
<p>Now, let's open the <code>config/voyager.php</code> file and update the storage disk to 'spaces':</p>
<pre><code class="hljsphp"><span class="hljs-string">'storage'</span> =&gt; [
    <span class="hljs-string">'disk'</span> =&gt; <span class="hljs-string">'spaces'</span>,
],
</code></pre>
<h2 id="step-4-overriding-default-voyager-blade-view-optional">Step 4: Overriding Default Voyager Blade View (Optional)</h2>
<p>In some older versions of Voyager, the media manager view had some issues with the file path.</p>
<p>To verify if you are still affected by this issue, visit the Voyager admin and if you see this error:</p>
<pre><code class="hljsperl">Found <span class="hljs-number">1</span> error <span class="hljs-keyword">while</span> validating the input provided <span class="hljs-keyword">for</span> the GetObject operation: [Key] expected string <span class="hljs-keyword">length</span> to be &gt;= <span class="hljs-number">1</span>, but found string <span class="hljs-keyword">length</span> of <span class="hljs-number">0</span>
</code></pre>
<p>The following steps will help you resolve this issue.</p>
<blockquote>
<p>NOTE: If you don't see the above error, you can skip this step.</p>
</blockquote>
<p>First, create a new directory for the view:</p>
<pre><code class="hljsperl"><span class="hljs-keyword">mkdir</span> -p resources/views/vendor/voyager/media
</code></pre>
<p>Next, copy the Voyager media manager view to your new directory:</p>
<pre><code class="hljs">cp vendor/tcg/voyager/resources/views/media/manager.blade.php resources/views/vendor/voyager/media
</code></pre>
<p>Finally, open the copied <code>manager.blade.php</code> file and update this line:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"img_icon"</span> <span class="hljs-attr">:style</span>=<span class="hljs-string">"imgIcon('{{ Storage::disk(config('voyager.storage.disk'))-&gt;url('/') }}'+file)"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>To:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"img_icon"</span> <span class="hljs-attr">:style</span>=<span class="hljs-string">"imgIcon('{{ Storage::disk(config('voyager.storage.disk'))-&gt;url('./') }}'+file)"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Note that the only change is the addition of a <code>./</code> before the <code>file</code> variable.</p>
<h2 id="conclusion">Conclusion</h2>
<p>After completing these steps, your Laravel Voyager application should now be set up to use DigitalOcean Spaces for file storage. This integration unlocks new possibilities for you, allowing you to scale your applications efficiently.</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>As a next step, you can try to upload a file using the media manager and check if it is uploaded to your DigitalOcean Spaces bucket.</p>
<p>Happy coding!</p>
]]></description>
                                                            <category>php</category>
                                            <category>laravel</category>
                                            <category>voyager</category>
                                            <category>wave</category>
                                            <category> DigitalOcean</category>
                                            <category>storage</category>
                                            <category>laravel voyager</category>
                                            <category>s3</category>
                                            <category>laravel wave</category>
                                            <category>spaces</category>
                                            <category>scale</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/10977</guid>
                <pubDate>Tue, 30 May 2023 23:39:03 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Difference between scaling horizontally and vertically for databases?]]></title>
                <link>https://devdojo.com/bobbyiliev/difference-between-scaling-horizontally-and-vertically-for-databases</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>When your application or website starts to receive more traffic, you'll need to think about how to scale your database to handle the additional load. There are two main ways to scale a database: vertically and horizontally. Let's dive in and understand the differences between these two approaches.</p>
<p><img src="https://imgur.com/mzJbAW8.png" alt=""></p>
<h2 id="toc-1-vertical-scaling-scale-up">1. Vertical Scaling (Scale-up)**</h2>
<p>Vertical scaling, also known as scaling up, involves adding more resources to your existing server to enhance its computational power. This could mean increasing the CPU power, RAM, SSD, or other hardware resources on your existing machine.</p>
<p><strong>Advantages of Vertical Scaling:</strong></p>
<ul>
<li>Simplicity: Vertical scaling is usually simpler as it involves just increasing the server's power. There's no need for code changes or complex architecture designs.</li>
<li>Data Consistency: Since there's only one database, there's no issue of data consistency.</li>
</ul>
<p><strong>Disadvantages of Vertical Scaling:</strong></p>
<ul>
<li>Limited Growth: There's a limit to how much you can scale up a single server. At some point, you won't be able to add any more resources.</li>
<li>Downtime: To upgrade the server, you might need to face some downtime unless your system supports hot-swappable components.</li>
<li>Cost: High-end servers can become very expensive.</li>
</ul>
<h2 id="toc-2-horizontal-scaling-scale-out">2. Horizontal Scaling (Scale-out)</h2>
<p>Horizontal scaling, also known as scaling out, involves adding more servers to your existing pool of servers. The load is distributed across multiple servers, thereby increasing the ability to handle more requests.</p>
<p><strong>Advantages of Horizontal Scaling:</strong></p>
<ul>
<li>Greater Capacity: Horizontal scaling allows for virtually limitless scaling, as you can always add more servers to handle more traffic.</li>
<li>Redundancy: Having multiple servers can increase the availability of your application. If one server fails, the others can take over.</li>
<li>Cost-effective: Rather than investing in a high-end server, you can distribute the load across multiple cost-effective servers.</li>
</ul>
<p><strong>Disadvantages of Horizontal Scaling:</strong></p>
<ul>
<li>Complexity: Horizontal scaling introduces complexity into your system. Your application code needs to handle distributing data across multiple servers, and this could require significant re-architecting of your application.</li>
<li>Data Consistency: When data is distributed across multiple servers, it's harder to maintain consistency. This is especially important for databases, where you want to ensure that all users are seeing the same data.</li>
</ul>
<h2 id="scaling-databases">Scaling Databases</h2>
<p>When it comes to databases, both scaling methods have their place.</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<ul>
<li>
<p>Vertical scaling can be a good option for databases with high data integrity requirements, such as financial systems. However, at a certain point, the cost and limitations of hardware may necessitate a move to horizontal scaling.</p>
</li>
<li>
<p>Horizontal scaling is great for databases with large amounts of data that can be easily partitioned. For example, a database for a social media site could partition data based on user ID, with different users' data stored on different servers.</p>
</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>In conclusion, the choice between vertical and horizontal scaling depends on the specific needs of your database and application. You may also use a combination of both methods for different parts of your application, using a technique known as hybrid scaling.</p>
<p>For more information on database scaling, I would recommend checking out this post here:</p>
<blockquote>
<p><a href="https://architecturenotes.co/database-sharding-explained/">Database Sharding Explained</a></p>
</blockquote>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
]]></description>
                                                            <category>mysql</category>
                                            <category>sql</category>
                                            <category>postgres</category>
                                            <category> data</category>
                                            <category>devops</category>
                                            <category>databases</category>
                                            <category>scaling</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/10933</guid>
                <pubDate>Sun, 14 May 2023 12:30:39 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to Create a Sticky NavBar using Tailwind CSS]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-create-a-sticky-navbar-using-tailwind-css</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>When building a modern, responsive website, navigation plays a crucial role in user experience. A sticky or affix navigation bar remains visible at the top of the screen as users scroll, making it easy for them to access the main menu items without having to scroll back to the top. Tailwind CSS, a popular utility-first CSS framework, makes it simple to create elegant, functional sticky navigation bars. In this tutorial, we'll walk you through the process of creating a sticky navigation bar using Tailwind CSS and showcase some stylish designs to inspire your project.</p>
<h2 id="step-1-setting-up-the-project">Step 1: Setting Up the Project</h2>
<p>Before we start, make sure you have Tailwind CSS installed in your project. You can either use the CLI, include it from a CDN, or set up a custom build. Visit the <a href="https://tailwindcss.com/docs/installation">official Tailwind CSS documentation</a> to learn how to set up Tailwind CSS for your specific project.</p>
<h2 id="step-2-creating-the-basic-navbar">Step 2: Creating the Basic NavBar</h2>
<p>First, let's create a simple navigation bar using Tailwind CSS. Add the following HTML code to your project:</p>
<pre><code class="hljsxml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/tailwindcss@2.2.16/dist/tailwind.min.css"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Sticky NavBar using Tailwind CSS<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-gray-100"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-white"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">nav</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container mx-auto px-6 py-3"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex justify-between items-center"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-2xl font-bold text-gray-800"</span>&gt;</span>MyWebsite<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex space-x-4"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-800"</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-800"</span>&gt;</span>About<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-800"</span>&gt;</span>Services<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-800"</span>&gt;</span>Contact<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Add your page content here --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>This code sets up a basic navigation bar with a container, logo, and menu links.</p>
<h2 id="step-3-making-the-navbar-sticky">Step 3: Making the NavBar Sticky</h2>
<p>To make the navigation bar sticky, we need to add the <code>fixed</code>, <code>top-0</code>, and <code>w-full</code> classes to the <code>&lt;header&gt;</code> element. This will fix the header at the top of the viewport and span the full width of the screen. Update your <code>&lt;header&gt;</code> tag as follows:</p>
<pre><code class="hljsjavascript">&lt;header <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"bg-white fixed top-0 w-full"</span>&gt;
</code></pre>
<p>Now, when you scroll down the page, the navigation bar will remain at the top.</p>
<h2 id="step-4-adding-a-shadow">Step 4: Adding a Shadow</h2>
<p>To create a subtle separation between the sticky navigation bar and the page content, add a shadow using the <code>shadow-md</code> class:</p>
<pre><code class="hljsjavascript">&lt;header <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"bg-white fixed top-0 w-full shadow-md"</span>&gt;
</code></pre>
<h2 id="step-5-design-inspirations">Step 5: Design Inspirations</h2>
<p>Now that you've created a basic sticky navigation bar using Tailwind CSS, you can customize its design by applying various utility classes or creating your own. Here are a few design ideas to inspire you:</p>
<ul>
<li>
<p>Transparent background with a color change on scroll: Add a transparent background to the navigation bar and change the background color when the user scrolls down, creating a smooth transition effect. You can achieve this by using JavaScript to toggle a class when the user scrolls.</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
</li>
<li>
<p>Hover effect on menu items: Enhance user experience by adding a hover effect to the menu items. Use the <code>hover:</code> prefix to change the text color or background color of the menu items when users hover over them. For example:</p>
</li>
</ul>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-800 hover:text-blue-600"</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<ul>
<li>Add a call-to-action button: Encourage users to take a specific action, such as signing up or contacting you, by adding a CTA button to the navigation bar. Use the <code>bg-</code>, <code>text-</code>, and <code>rounded-</code> classes to style the button:</li>
</ul>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-blue-600 text-white px-4 py-2 rounded-md"</span>&gt;</span>Sign Up<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<ul>
<li>
<p>Responsive design with a hamburger menu: For mobile devices, you can create a responsive design by hiding the menu items and displaying a hamburger menu instead. Use Tailwind CSS's responsive classes (e.g., <code>lg:hidden</code> and <code>lg:flex</code>) and JavaScript to toggle the mobile menu.</p>
</li>
<li>
<p>Add a search bar: Enhance your sticky navigation bar by integrating a search bar. Use the <code>border-</code>, <code>rounded-</code>, and <code>focus:</code> classes to style the input field and search button:</p>
</li>
</ul>
<pre><code class="hljsjavascript">&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"flex items-center space-x-2"</span>&gt;
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"search"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Search"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:border-blue-600"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-blue-600 text-white px-4 py-2 rounded-md"</span>&gt;</span>Search<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<h2 id="complete-example">Complete example</h2>
<p>Here is the complete HTML code for a basic sticky navigation bar with some additional design elements, including hover effects, a CTA button, and a search bar:</p>
<pre><code class="hljsxml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/tailwindcss@2.2.16/dist/tailwind.min.css"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Sticky NavBar using Tailwind CSS<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-gray-100"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-white fixed top-0 w-full shadow-md"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">nav</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container mx-auto px-6 py-3"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex justify-between items-center"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-2xl font-bold text-gray-800"</span>&gt;</span>MyWebsite<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hidden md:flex items-center space-x-4"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-800 hover:text-blue-600"</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-800 hover:text-blue-600"</span>&gt;</span>About<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-800 hover:text-blue-600"</span>&gt;</span>Services<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-800 hover:text-blue-600"</span>&gt;</span>Contact<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-blue-600 text-white px-4 py-2 rounded-md"</span>&gt;</span>Sign Up<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"md:hidden flex items-center"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-800 focus:outline-none"</span>&gt;</span> <span class="hljs-comment">&lt;!-- Add a hamburger menu icon here --&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-6 h-6"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">stroke-linecap</span>=<span class="hljs-string">"round"</span> <span class="hljs-attr">stroke-linejoin</span>=<span class="hljs-string">"round"</span> <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"2"</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M4 6h16M4 12h16M4 18h16"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">path</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-4"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex items-center space-x-2"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"search"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Search"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:border-blue-600 w-full"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-blue-600 text-white px-4 py-2 rounded-md"</span>&gt;</span>Search<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Add your page content here --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>This example includes hover effects for menu items, a CTA button, and a search bar. You can further customize the design by applying different utility classes or creating your own. Remember to test your designs on various devices and screen sizes to ensure a seamless experience for all users.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In this tutorial, we demonstrated how to create a sticky navigation bar using Tailwind CSS and provided some design ideas to help you customize the look and feel of your NavBar.</p>
<p>By implementing a sticky navigation bar on your website, you can improve user experience and ensure that essential menu items are always easily accessible. To make the design process even more efficient and enjoyable, you can use the <a href="https://devdojo.com/tails">Tails Tailwind CSS page builder</a>, which offers a comprehensive library of pre-built UI components, all created with Tailwind CSS.</p>
<p>This intuitive, user-friendly page builder allows you to effortlessly create and customize your website designs using a visual interface, without having to write a single line of code. Remember to test your designs on various devices and screen sizes to ensure a seamless experience for all users.</p>
]]></description>
                                                            <category>css</category>
                                            <category>webdev</category>
                                            <category>tailwind css</category>
                                            <category>design</category>
                                            <category>sticky navigation bar</category>
                                            <category>affix navbar</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/10873</guid>
                <pubDate>Sun, 23 Apr 2023 02:50:37 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[7 Best VS Code Extensions for Python Developers]]></title>
                <link>https://devdojo.com/bobbyiliev/7-best-vs-code-extensions-for-python-developers</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Visual Studio Code (VS Code) has become a popular choice for Python developers due to its flexibility, extensibility, and user-friendly interface. One of the key features of VS Code is its extensive collection of extensions that can significantly improve your development experience.</p>
<p>In this post, we will highlight the 7 best VS Code extensions for Python developers to help you write better code, increase productivity, and enhance your overall coding experience.</p>
<h3 id="toc-1-python-by-microsoft">1. <a href="https://marketplace.visualstudio.com/items?itemName=ms-python.python">Python (by Microsoft)</a></h3>
<p><img src="https://imgur.com/gTzeVT3.gif" alt=""></p>
<p>The Python extension by Microsoft is a must-have for any Python developer using VS Code. It provides a rich set of features, including:</p>
<ul>
<li>IntelliSense (code completion)</li>
<li>Linting (error checking)</li>
<li>Debugging</li>
<li>Code navigation</li>
<li>Unit testing</li>
<li>Jupyter Notebooks support</li>
<li>Code refactoring</li>
</ul>
<h3 id="toc-2-pylance">2. <a href="https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance">Pylance</a></h3>
<p><img src="https://imgur.com/nF6v48p.gif" alt=""></p>
<p>Pylance is a fast and feature-rich language server for Python that provides excellent IntelliSense capabilities. It enhances your development experience with:</p>
<ul>
<li>Type checking</li>
<li>Autocompletion</li>
<li>Type inference</li>
<li>Automatic imports</li>
<li>Signature help</li>
<li>Code navigation</li>
</ul>
<p>Pylance works best when used alongside the Python extension by Microsoft.</p>
<h3 id="toc-3-live-share">3. <a href="https://marketplace.visualstudio.com/items?itemName=MS-vsliveshare.vsliveshare">Live Share</a></h3>
<p>Visual Studio Live Share is an extension that allows real-time collaboration with other developers, enabling you to share your code, workspace, and even terminal instances with others. This powerful tool is excellent for remote pair programming, code reviews, and group debugging sessions.</p>
<p>With Live Share, you can:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<ul>
<li>Share your workspace with read or write access</li>
<li>Co-edit and co-debug code</li>
<li>Share a local server and terminal instances</li>
<li>Communicate with collaborators using integrated audio and text chat</li>
</ul>
<p>Live Share supports Python development and seamlessly integrates with the Python extension by Microsoft, making it an invaluable tool for Python developers working in teams or on collaborative projects.</p>
<h3 id="toc-4-code-runner">4. <a href="https://marketplace.visualstudio.com/items?itemName=formulahendry.code-runner">Code Runner</a></h3>
<p><img src="https://imgur.com/44lcNax.gif" alt=""></p>
<p>Code Runner is a lightweight extension that allows you to run code snippets in various languages, including Python, with just a simple click or keyboard shortcut. It is extremely helpful for quickly testing your code without having to switch to the terminal or execute the entire script.</p>
<h3 id="toc-5-better-comments">5. <a href="https://marketplace.visualstudio.com/items?itemName=aaron-bond.better-comments">Better Comments</a></h3>
<p><img src="https://imgur.com/PNt9CDv.png" alt=""></p>
<p>Better Comments is an extension that helps you create more human-friendly and visually distinguishable comments in your code. It supports different comment categories like queries, alerts, and highlights, making it easier to navigate through your comments and understand the purpose behind each one.</p>
<h3 id="toc-6-gitlens-git-supercharged">6. <a href="https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens">GitLens — Git supercharged</a></h3>
<p><img src="https://imgur.com/Z6H7I0T.gif" alt=""></p>
<p>GitLens is a powerful extension that provides Git integration right inside VS Code. It offers various features to enhance your workflow, such as:</p>
<ul>
<li>Inline Git blame annotations</li>
<li>Commit search and comparison</li>
<li>Git history explorer</li>
<li>Branch and commit visualization</li>
<li>Support for remote repositories (GitHub, GitLab, and more)</li>
</ul>
<p>GitLens can be a game-changer for Python developers working on large projects or collaborating with a team.</p>
<h3 id="toc-7-python-docstring-generator">7. <a href="https://marketplace.visualstudio.com/items?itemName=njpwerner.autodocstring">Python Docstring Generator</a></h3>
<p><img src="https://imgur.com/7zs7Vw9.gif" alt=""></p>
<p>Python Docstring Generator is a helpful extension that automatically generates docstrings for your Python functions and methods following popular docstring conventions, such as Google, NumPy, and reStructuredText. This extension saves time and ensures consistent documentation across your codebase.</p>
<p>To use this extension, simply place the cursor within a function or method definition and press the default keybinding Ctrl+Alt+D followed by Ctrl+Alt+S. The generated docstring will appear, and you can easily fill in the necessary details.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The 7 extensions mentioned in this post are essential for any Python developer using VS Code. They will help you write better code, increase productivity, and enhance your overall coding experience. By installing these extensions, you'll be well-equipped to tackle a wide range of Python development tasks more efficiently.</p>
<p>Remember, the VS Code marketplace offers countless other extensions that cater to specific needs and preferences. Feel free to explore the available extensions to further tailor your development environment to your unique requirements. Happy coding!</p>
]]></description>
                                                            <category>Python</category>
                                            <category> coding</category>
                                            <category>development</category>
                                            <category>vs-code</category>
                                            <category>web dev</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/10829</guid>
                <pubDate>Mon, 10 Apr 2023 05:46:45 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Introduction to Structs and Implementations in Rust]]></title>
                <link>https://devdojo.com/bobbyiliev/introduction-to-structs-and-implementations-in-rust</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Structs in Rust are a way to create custom data types that group together related pieces of data. This tutorial will introduce you to the basics of structs in Rust, including defining, instantiating, and accessing their data. By the end of this tutorial, you'll have a solid understanding of how to use structs in your Rust projects.</p>
<h2 id="structs">Structs</h2>
<p>To define a struct, use the <code>struct</code> keyword followed by the name of the struct and its fields within curly braces <code>{}</code>. Each field has a name and a type, separated by a colon:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">struct</span> <span class="hljs-selector-tag">User</span> {
    <span class="hljs-attribute">username</span>: String,
    email: String,
    age: u8,
    active: bool,
}
</code></pre>
<p>In this example, we've defined a User struct with four fields: <code>username</code>, <code>email</code>, <code>age</code>, and <code>active</code>.</p>
<h2 id="creating-instances-of-a-struct">Creating Instances of a Struct</h2>
<p>To create an instance of a struct, specify the name of the struct followed by the field values inside curly braces <code>{}</code>:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">let</span> user1 = User {
    <span class="hljs-attr">username</span>: <span class="hljs-built_in">String</span>::<span class="hljs-keyword">from</span>(<span class="hljs-string">"Alice"</span>),
    <span class="hljs-attr">email</span>: <span class="hljs-built_in">String</span>::<span class="hljs-keyword">from</span>(<span class="hljs-string">"alice@example.com"</span>),
    <span class="hljs-attr">age</span>: <span class="hljs-number">30</span>,
    <span class="hljs-attr">active</span>: <span class="hljs-literal">true</span>,
};
</code></pre>
<p>Here, we've created an instance of the User struct called user1 with the specified field values.</p>
<h2 id="accessing-struct-fields">Accessing Struct Fields</h2>
<p>To access the value of a field in a struct, use the dot notation:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">println</span>!("<span class="hljs-selector-tag">Username</span>: {}", <span class="hljs-selector-tag">user1</span><span class="hljs-selector-class">.username</span>);
<span class="hljs-selector-tag">println</span>!("<span class="hljs-selector-tag">Email</span>: {}", <span class="hljs-selector-tag">user1</span><span class="hljs-selector-class">.email</span>);
<span class="hljs-selector-tag">println</span>!("<span class="hljs-selector-tag">Age</span>: {}", <span class="hljs-selector-tag">user1</span><span class="hljs-selector-class">.age</span>);
<span class="hljs-selector-tag">println</span>!("<span class="hljs-selector-tag">Active</span>: {}", <span class="hljs-selector-tag">user1</span><span class="hljs-selector-class">.active</span>);
</code></pre>
<p>This will output the following:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">Username</span>: <span class="hljs-selector-tag">Alice</span>
<span class="hljs-selector-tag">Email</span>: <span class="hljs-selector-tag">alice</span><span class="hljs-keyword">@example</span>.com
<span class="hljs-attribute">Age:</span> <span class="hljs-number">30</span>
<span class="hljs-attribute">Active:</span> true
</code></pre>
<h2 id="mutable-struct-instances">Mutable Struct Instances</h2>
<p>By default, struct instances are immutable. To make a mutable instance, use the <code>mut</code> keyword:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">let</span> mut user2 = User {
    <span class="hljs-attr">username</span>: <span class="hljs-built_in">String</span>::<span class="hljs-keyword">from</span>(<span class="hljs-string">"Bob"</span>),
    <span class="hljs-attr">email</span>: <span class="hljs-built_in">String</span>::<span class="hljs-keyword">from</span>(<span class="hljs-string">"bob@example.com"</span>),
    <span class="hljs-attr">age</span>: <span class="hljs-number">25</span>,
    <span class="hljs-attr">active</span>: <span class="hljs-literal">false</span>,
};
</code></pre>
<p>To update a field's value in a mutable struct, use the dot notation:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<pre><code class="hljsgo">user2.age = <span class="hljs-number">26</span>;
<span class="hljs-built_in">println</span>!(<span class="hljs-string">"Updated Age: {}"</span>, user2.age);
</code></pre>
<p>Output:</p>
<pre><code class="hljs">Updated Age: 26
</code></pre>
<h2 id="using-functions-with-structs">Using Functions with Structs</h2>
<p>Functions can accept and return struct instances. Here's an example of a function that takes a User struct and returns a String:</p>
<pre><code class="hljsjavascript">fn format_user(user: &amp;User) -&gt; <span class="hljs-built_in">String</span> {
    format!(<span class="hljs-string">"{} ({}) - {}"</span>, user.username, user.age, user.email)
}

<span class="hljs-keyword">let</span> user_info = format_user(&amp;user1);
println!(<span class="hljs-string">"{}"</span>, user_info);
</code></pre>
<p>This will output:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">Alice</span> (30) <span class="hljs-selector-tag">-</span> <span class="hljs-selector-tag">alice</span><span class="hljs-keyword">@example</span>.com
</code></pre>
<h2 id="implementations">Implementations</h2>
<p>Implementations allow you to define methods and associated functions for a struct. Use the impl keyword followed by the name of the struct and a block containing the method and associated function definitions.</p>
<pre><code class="hljsgo"><span class="hljs-keyword">struct</span> User {
    username: String,
    email: String,
    age: u8,
    active: <span class="hljs-keyword">bool</span>,
}

impl User {
    <span class="hljs-comment">// Method and associated function definitions go here</span>
}
</code></pre>
<h2 id="methods">Methods:</h2>
<p>Methods are functions associated with instances of a struct. To define a method, write a function within the impl block. The first parameter of a method is always self, which represents the instance the method is called on. Here's an example of a method that returns a formatted string for a User instance:</p>
<pre><code class="hljsruby">impl User {
    fn format_info(&amp;<span class="hljs-keyword">self</span>) -&gt; String {
        format!(<span class="hljs-string">"{} ({}) - {}"</span>, <span class="hljs-keyword">self</span>.username, <span class="hljs-keyword">self</span>.age, <span class="hljs-keyword">self</span>.email)
    }
}
</code></pre>
<p>To call a method, use the dot notation:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">let</span> user1 = User {
    <span class="hljs-attr">username</span>: <span class="hljs-built_in">String</span>::<span class="hljs-keyword">from</span>(<span class="hljs-string">"Alice"</span>),
    <span class="hljs-attr">email</span>: <span class="hljs-built_in">String</span>::<span class="hljs-keyword">from</span>(<span class="hljs-string">"alice@example.com"</span>),
    <span class="hljs-attr">age</span>: <span class="hljs-number">30</span>,
    <span class="hljs-attr">active</span>: <span class="hljs-literal">true</span>,
};

<span class="hljs-keyword">let</span> user_info = user1.format_info();
println!(<span class="hljs-string">"{}"</span>, user_info);
</code></pre>
<p>This will output:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">Alice</span> (30) <span class="hljs-selector-tag">-</span> <span class="hljs-selector-tag">alice</span><span class="hljs-keyword">@example</span>.com
</code></pre>
<h2 id="associated-functions">Associated Functions</h2>
<p>Associated functions are similar to methods, but they don't have an instance of the struct as their first parameter. They are often used as constructors to create new instances of the struct. To define an associated function, write a function within the <code>impl</code> block without the <code>self</code> parameter:</p>
<pre><code class="hljsjavascript">impl User {
    fn <span class="hljs-keyword">new</span>(username: <span class="hljs-built_in">String</span>, <span class="hljs-attr">email</span>: <span class="hljs-built_in">String</span>, <span class="hljs-attr">age</span>: u8) -&gt; Self {
        User {
            username,
            email,
            age,
            <span class="hljs-attr">active</span>: <span class="hljs-literal">true</span>,
        }
    }
}
</code></pre>
<p>To call an associated function, use the double colon notation:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">let</span> user2 = User::<span class="hljs-keyword">new</span>(
    <span class="hljs-built_in">String</span>::<span class="hljs-keyword">from</span>(<span class="hljs-string">"Bob"</span>),
    <span class="hljs-attr">String</span>::<span class="hljs-keyword">from</span>(<span class="hljs-string">"bob@example.com"</span>),
    <span class="hljs-number">25</span>,
);

println!(<span class="hljs-string">"{}"</span>, user2.format_info());
</code></pre>
<p>This will output:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">Bob</span> (25) <span class="hljs-selector-tag">-</span> <span class="hljs-selector-tag">bob</span><span class="hljs-keyword">@example</span>.com
</code></pre>
<h2 id="implementing-traits-for-structs">Implementing Traits for Structs</h2>
<p>Traits are a way to define shared behavior between different structs in Rust. By implementing a trait for a struct, you can provide a common interface for different data types. Here's an example of implementing the <code>std::fmt::Display</code> trait for the <code>User</code> struct:</p>
<pre><code class="hljsphp"><span class="hljs-keyword">use</span> <span class="hljs-title">std</span>::<span class="hljs-title">fmt</span>;

impl fmt::Display <span class="hljs-keyword">for</span> User {
    fn fmt(&amp;<span class="hljs-keyword">self</span>, f: &amp;mut fmt::Formatter) -&gt; fmt::Result {
        write!(f, <span class="hljs-string">"{} ({}) - {}"</span>, <span class="hljs-keyword">self</span>.username, <span class="hljs-keyword">self</span>.age, <span class="hljs-keyword">self</span>.email)
    }
}
</code></pre>
<p>Now you can use the User struct with any function or macro that requires the Display trait, like println!():</p>
<pre><code class="hljsgo"><span class="hljs-built_in">println</span>!(<span class="hljs-string">"{}"</span>, user1);
</code></pre>
<p>This will output:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">Alice</span> (30) <span class="hljs-selector-tag">-</span> <span class="hljs-selector-tag">alice</span><span class="hljs-keyword">@example</span>.com
</code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>In this tutorial, you've learned the basics of working with structs in Rust, including defining, creating, and modifying struct instances, as well as using functions with structs. Structs are a powerful way to group related data together and make your code more organized and maintainable. As you continue to explore Rust, you'll find that structs are a fundamental building block for creating complex data structures and handling real-world problems.</p>
<p>You've also learned how to extend structs in Rust with implementations, methods, and associated functions. Additionally, you've discovered how to implement traits for structs, allowing you to define shared behavior between different data types. These techniques will enable you to create more complex and powerful data structures in Rust while keeping your code organized and maintainable.</p>
<p>Happy coding!</p>
]]></description>
                                                            <category>programming</category>
                                            <category>code</category>
                                            <category>rust</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/10828</guid>
                <pubDate>Mon, 10 Apr 2023 04:10:22 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[8 Awesome VS Code Extensions for JavaScript Developers]]></title>
                <link>https://devdojo.com/bobbyiliev/8-awesome-vs-code-extensions-for-javascript-developers</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Visual Studio Code (VS Code) is a popular, lightweight, and powerful source code editor developed by Microsoft. It has extensive support for JavaScript and TypeScript, making it the go-to choice for many developers. One of the most significant features of VS Code is its extensibility, allowing you to add custom extensions to enhance your development experience.</p>
<p>In this tutorial, we will explore 8 awesome VS Code extensions for JavaScript developers.</p>
<h2 id="eslint"><a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint">ESLint</a></h2>
<p>ESLint is a widely used linting tool for JavaScript that analyzes your code and identifies potential problems, such as syntax errors, performance issues, and coding standard violations. This extension integrates ESLint into VS Code, providing real-time feedback and highlighting issues in your code as you type.</p>
<h2 id="prettier-code-formatted"><a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode">Prettier - Code formatted</a></h2>
<p>Prettier is an opinionated code formatter that supports JavaScript, TypeScript, and many other languages. The Prettier extension for VS Code formats your code automatically on save or manually via a command, ensuring consistent code style across your project.</p>
<h2 id="debugger-for-chrome"><a href="https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome">Debugger for Chrome</a></h2>
<p>Debugger for Chrome connects the Google Chrome debugger to VS Code, allowing you to debug your JavaScript code directly within the editor. With this extension, you can set breakpoints, step through code, inspect variables, and more without leaving VS Code.</p>
<p><img src="https://imgur.com/JhTU1Hn.gif" alt=""></p>
<h2 id="npm-intellisense"><a href="https://marketplace.visualstudio.com/items?itemName=christian-kohler.npm-intellisense">npm Intellisense</a></h2>
<p>npm Intellisense is a must-have for developers who work with Node.js and npm packages. This extension provides autocompletion for npm modules in your code, making it easy to import and require packages without having to memorize package names.</p>
<p><img src="https://imgur.com/NtEwlW5.gif" alt=""></p>
<h2 id="gitlens-git-supercharged"><a href="https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens">GitLens — Git supercharged</a></h2>
<p>GitLens is an extension that supercharges Git functionality in VS Code. It provides features like inline blame annotations, commit searching, and file history explorers. GitLens is especially useful for JavaScript developers working in large teams or on open-source projects, as it helps you navigate and understand codebases faster.</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<p><img src="https://imgur.com/Z6H7I0T.gif" alt=""></p>
<h2 id="import-cost"><a href="https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost">Import Cost</a></h2>
<p>Import Cost is a useful extension that displays the size of imported packages inline, helping you keep track of your application's bundle size. With this extension, you can make informed decisions about adding or removing dependencies based on their impact on the overall size of your project.</p>
<p><img src="https://imgur.com/JuHJMIt.gif" alt=""></p>
<h2 id="live-server"><a href="https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer">Live Server</a></h2>
<p>Live Server is a simple but powerful extension that launches a local development server with live reloading for static and dynamic pages. This is especially useful for front-end JavaScript developers, as it allows you to see changes in real-time without manually refreshing the browser.</p>
<p><img src="https://imgur.com/qEtkNX9.gif" alt=""></p>
<h2 id="rest-client"><a href="https://marketplace.visualstudio.com/items?itemName=humao.rest-client">REST Client</a></h2>
<p>REST Client is a convenient VS Code extension that lets you send HTTP requests and view responses directly within the editor. It's great for JavaScript developers working with APIs, as it allows you to test and debug endpoints without needing a separate tool like Postman.</p>
<p><img src="https://imgur.com/14sToIi.gif" alt=""></p>
<h2 id="conclusion">Conclusion</h2>
<p>These 8 awesome VS Code extensions can significantly improve your productivity and development experience as a JavaScript developer. By leveraging these tools, you'll be able to write cleaner, more efficient code, debug your applications more effectively, and more.</p>
<p>Happy coding!</p>
]]></description>
                                                            <category>javascript</category>
                                            <category>nodejs</category>
                                            <category>node</category>
                                            <category>TypeScript</category>
                                            <category>webdev</category>
                                            <category>vs-code</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/10797</guid>
                <pubDate>Mon, 03 Apr 2023 05:00:51 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Simulating Streaming Data for Fraud Detection with Datagen CLI]]></title>
                <link>https://devdojo.com/bobbyiliev/simulating-streaming-data-for-fraud-detection-with-datagen-cli</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Building and testing a real-time fraud detection application requires a continuous stream of realistic data. But generating that data can be a challenge. That's why we recently created the <a href="https://github.com/MaterializeInc/datagen">Datagen CLI</a>, a simple tool that helps you create believable fake data using the FakerJS API.</p>
<p>In this blog post, we'll explore how to use the Datagen CLI to simulate a streaming data use-case like a fraud detection app. I'll show you how to install and configure the tool, create a schema for the data, and send the data to a Kafka topic. By the end of this tutorial, you'll be able to generate your own realistic streaming data for testing and development purposes.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>Basic knowledge of Kafka and streaming data</li>
<li>Node.js and npm installed on your machine</li>
<li>A Kafka cluster set up and running</li>
</ul>
<h2 id="installation-and-setup">Installation and Setup</h2>
<p>First, install the Datagen CLI using npm:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">npm</span> <span class="hljs-selector-tag">install</span> <span class="hljs-selector-tag">-g</span> <span class="hljs-keyword">@materializeinc</span>/datagen
</code></pre>
<p>Create a .env file in your working directory with the necessary Kafka and Schema Registry environment variables. Replace the placeholder values with your actual settings:</p>
<pre><code class="hljsmarkdown"><span class="hljs-section"># Kafka Brokers</span>
KAFKA_BROKERS=

<span class="hljs-section"># For Kafka SASL Authentication:</span>
SASL_USERNAME=
SASL_PASSWORD=
SASL_MECHANISM=
</code></pre>
<h2 id="creating-a-schema-for-fraud-detection-data">Creating a Schema for Fraud Detection Data</h2>
<p>To generate realistic data for a fraud detection app, we need to define a schema that includes relevant fields like transaction ID, user ID, timestamp, and transaction amount. Let's create a JSON schema called <code>transactions.json</code>:</p>
<pre><code class="hljsgo">[
  {
    <span class="hljs-string">"_meta"</span>: {
      <span class="hljs-string">"topic"</span>: <span class="hljs-string">"transactions"</span>
    },
    <span class="hljs-string">"transaction_id"</span>: <span class="hljs-string">"faker.datatype.uuid()"</span>,
    <span class="hljs-string">"user_id"</span>: <span class="hljs-string">"faker.datatype.number({min: 1, max: 10000})"</span>,
    <span class="hljs-string">"timestamp"</span>: <span class="hljs-string">"faker.date.between('2023-01-01', '2023-12-31')"</span>,
    <span class="hljs-string">"amount"</span>: <span class="hljs-string">"faker.finance.amount(0, 10000, 2)"</span>,
    <span class="hljs-string">"is_fraud"</span>: <span class="hljs-string">"faker.datatype.boolean()"</span>
  }
]
</code></pre>
<p>This schema generates a stream of transaction data with random transaction IDs, user IDs, timestamps, and amounts. We've also added a field called <code>is_fraud</code> that randomly the transactions as fraudulent.</p>
<h2 id="generating-and-sending-data-to-kafka">Generating and Sending Data to Kafka</h2>
<p>Now that we have our schema, we can use the Datagen CLI to generate data and send it to a Kafka topic. Use the following command to generate an infinite stream of transactions in JSON format:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">datagen</span> \
  <span class="hljs-selector-tag">-s</span> <span class="hljs-selector-tag">transactions</span><span class="hljs-selector-class">.json</span> \
  <span class="hljs-selector-tag">-f</span> <span class="hljs-selector-tag">json</span> \
  <span class="hljs-selector-tag">-n</span> <span class="hljs-selector-tag">-1</span> \
  <span class="hljs-selector-tag">-dr</span>
</code></pre>
<p>The <code>-n</code> flag specifies the number of messages to generate. We've set it to <code>-1</code> to generate an infinite stream of data. The <code>-dr</code> flag enables dry run mode, which prints the data to the console instead of sending it to Kafka. This is useful for testing and debugging.</p>
<p>Example output:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<pre><code class="hljsjavascript">✔  Dry run: Skipping record production...  
  Topic: transactions 
  Record key: <span class="hljs-literal">null</span> 
  <span class="hljs-attr">Payload</span>: {<span class="hljs-string">"transaction_id"</span>:<span class="hljs-string">"b86d1d57-a650-4680-843d-06179f1c4c2e"</span>,<span class="hljs-string">"user_id"</span>:<span class="hljs-number">5127</span>,<span class="hljs-string">"timestamp"</span>:<span class="hljs-string">"2023-09-02T03:26:28.194Z"</span>,<span class="hljs-string">"amount"</span>:<span class="hljs-string">"6904.40"</span>,<span class="hljs-string">"is_fraud"</span>:<span class="hljs-literal">false</span>}


✔  Dry run: Skipping record production...  
  Topic: transactions 
  Record key: <span class="hljs-literal">null</span> 
  <span class="hljs-attr">Payload</span>: {<span class="hljs-string">"transaction_id"</span>:<span class="hljs-string">"719fe62a-322c-4b58-89f9-e380e2f3552d"</span>,<span class="hljs-string">"user_id"</span>:<span class="hljs-number">2757</span>,<span class="hljs-string">"timestamp"</span>:<span class="hljs-string">"2023-09-30T06:40:37.378Z"</span>,<span class="hljs-string">"amount"</span>:<span class="hljs-string">"3375.15"</span>,<span class="hljs-string">"is_fraud"</span>:<span class="hljs-literal">true</span>}
</code></pre>
<p>Press Ctrl+C to stop producing data.</p>
<h2 id="enriching-the-data">Enriching the Data</h2>
<p>To enrich the <code>datagen</code> schema example, we can add more fields related to geolocation and other attributes that can be useful for fraud detection. Update the JSON input schema as follows:</p>
<pre><code class="hljsgo">[
  {
    <span class="hljs-string">"_meta"</span>: {
      <span class="hljs-string">"topic"</span>: <span class="hljs-string">"transactions"</span>,
      <span class="hljs-string">"key"</span>: <span class="hljs-string">"id"</span>
    },
    <span class="hljs-string">"id"</span>: <span class="hljs-string">"faker.datatype.uuid()"</span>,
    <span class="hljs-string">"user_id"</span>: <span class="hljs-string">"faker.datatype.number({min: 1, max: 1000})"</span>,
    <span class="hljs-string">"amount"</span>: <span class="hljs-string">"faker.finance.amount(1, 5000, 2)"</span>,
    <span class="hljs-string">"currency"</span>: <span class="hljs-string">"faker.finance.currencyCode()"</span>,
    <span class="hljs-string">"timestamp"</span>: <span class="hljs-string">"faker.date.past(1, '2023-01-01').getTime()"</span>,
    <span class="hljs-string">"is_fraud"</span>: <span class="hljs-string">"faker.datatype.boolean({likelihood: 5})"</span>,
    <span class="hljs-string">"ip_address"</span>: <span class="hljs-string">"faker.internet.ip()"</span>,
    <span class="hljs-string">"location"</span>: {
      <span class="hljs-string">"latitude"</span>: <span class="hljs-string">"faker.address.latitude()"</span>,
      <span class="hljs-string">"longitude"</span>: <span class="hljs-string">"faker.address.longitude()"</span>
    },
    <span class="hljs-string">"device"</span>: {
      <span class="hljs-string">"id"</span>: <span class="hljs-string">"faker.datatype.uuid()"</span>,
      <span class="hljs-string">"type"</span>: <span class="hljs-string">"faker.helpers.arrayElement(['mobile', 'tablet', 'desktop'])"</span>,
      <span class="hljs-string">"os"</span>: <span class="hljs-string">"faker.helpers.arrayElement(['ios', 'android', 'windows', 'macos', 'linux', 'other'])"</span>
    },
    <span class="hljs-string">"merchant_id"</span>: <span class="hljs-string">"faker.datatype.number({min: 1, max: 500})"</span>
  }
]
</code></pre>
<p>In this enriched schema, we've added:</p>
<ul>
<li><code>ip_address</code>: An IP address related to the transaction.</li>
<li><code>location</code>: An object containing latitude and longitude.</li>
<li><code>device</code>: An object containing device information such as ID, type, and operating system.</li>
<li><code>merchant_id</code>: A unique ID representing the merchant involved in the transaction.</li>
</ul>
<h2 id="relationship-between-transactions-and-users">Relationship between Transactions and Users</h2>
<p>The Datagen CLI can also generate data for related entities.</p>
<p>For example, we can extend the schema to include a <code>users</code> topic that contains user information. We can then use the <code>user_id</code> field in the <code>transactions</code> topic to join the two topics together:</p>
<pre><code class="hljsgo">[
  {
    <span class="hljs-string">"_meta"</span>: {
      <span class="hljs-string">"topic"</span>: <span class="hljs-string">"users"</span>,
      <span class="hljs-string">"key"</span>: <span class="hljs-string">"id"</span>,
      <span class="hljs-string">"relationships"</span>: [
        {
          <span class="hljs-string">"topic"</span>: <span class="hljs-string">"transactions"</span>,
          <span class="hljs-string">"parent_field"</span>: <span class="hljs-string">"id"</span>,
          <span class="hljs-string">"child_field"</span>: <span class="hljs-string">"user_id"</span>,
          <span class="hljs-string">"records_per"</span>: <span class="hljs-number">10</span>
        }
      ]
    },
    <span class="hljs-string">"id"</span>: <span class="hljs-string">"faker.datatype.number({min: 1, max: 1000})"</span>,
    <span class="hljs-string">"name"</span>: <span class="hljs-string">"faker.name.fullName()"</span>,
    <span class="hljs-string">"email"</span>: <span class="hljs-string">"faker.internet.email()"</span>,
    <span class="hljs-string">"registered_at"</span>: <span class="hljs-string">"faker.date.past(5, '2023-01-01').getTime()"</span>
  },
  {
    <span class="hljs-string">"_meta"</span>: {
      <span class="hljs-string">"topic"</span>: <span class="hljs-string">"transactions"</span>,
      <span class="hljs-string">"key"</span>: <span class="hljs-string">"id"</span>
    },
    <span class="hljs-string">"id"</span>: <span class="hljs-string">"faker.datatype.uuid()"</span>,
    <span class="hljs-string">"user_id"</span>: <span class="hljs-string">"faker.datatype.number(100)"</span>,
    <span class="hljs-string">"amount"</span>: <span class="hljs-string">"faker.finance.amount(1, 5000, 2)"</span>,
    <span class="hljs-string">"currency"</span>: <span class="hljs-string">"faker.finance.currencyCode()"</span>,
    <span class="hljs-string">"timestamp"</span>: <span class="hljs-string">"faker.date.between('relationship.registered_at', new Date()).getTime()"</span>,
    <span class="hljs-string">"is_fraud"</span>: <span class="hljs-string">"faker.datatype.boolean({likelihood: 5})"</span>,
    <span class="hljs-string">"ip_address"</span>: <span class="hljs-string">"faker.internet.ip()"</span>,
    <span class="hljs-string">"location"</span>: {
      <span class="hljs-string">"latitude"</span>: <span class="hljs-string">"faker.address.latitude()"</span>,
      <span class="hljs-string">"longitude"</span>: <span class="hljs-string">"faker.address.longitude()"</span>
    },
    <span class="hljs-string">"device"</span>: {
      <span class="hljs-string">"id"</span>: <span class="hljs-string">"faker.datatype.uuid()"</span>,
      <span class="hljs-string">"type"</span>: <span class="hljs-string">"faker.helpers.arrayElement(['mobile', 'tablet', 'desktop'])"</span>,
      <span class="hljs-string">"os"</span>: <span class="hljs-string">"faker.helpers.arrayElement(['ios', 'android', 'windows', 'macos', 'linux', 'other'])"</span>
    },
    <span class="hljs-string">"merchant_id"</span>: <span class="hljs-string">"faker.datatype.number({min: 1, max: 500})"</span>
  }
]
</code></pre>
<p>The data will be produced to the <code>users</code> and <code>transactions</code> topics. The <code>transactions</code> topic will contain a <code>user_id</code> field that references the <code>id</code> field in the <code>users</code> topic. The <code>transactions</code> topic will also contain a <code>registered_at</code> field that references the <code>registered_at</code> field in the <code>users</code> topic.</p>
<p>An example of the data produced:</p>
<pre><code class="hljsgo">...
  Topic: users 
  Record key: <span class="hljs-number">602</span> 
  Payload: {<span class="hljs-string">"id"</span>:<span class="hljs-number">602</span>,<span class="hljs-string">"name"</span>:<span class="hljs-string">"Mr. Jennie Prohaska"</span>,<span class="hljs-string">"email"</span>:<span class="hljs-string">"Natalie60@hotmail.com"</span>,<span class="hljs-string">"registered_at"</span>:<span class="hljs-number">1591058898886</span>}


  Topic: transactions 
  Record key: <span class="hljs-number">417</span>f6a6b-d7c5<span class="hljs-number">-47</span>a0-a013<span class="hljs-number">-93</span>f2aee94941 
  Payload: {<span class="hljs-string">"user_id"</span>:<span class="hljs-number">602</span>,<span class="hljs-string">"id"</span>:<span class="hljs-string">"417f6a6b-d7c5-47a0-a013-93f2aee94941"</span>,<span class="hljs-string">"amount"</span>:<span class="hljs-string">"1760.29"</span>,<span class="hljs-string">"currency"</span>:<span class="hljs-string">"MZN"</span>,<span class="hljs-string">"timestamp"</span>:<span class="hljs-number">1680516946423</span>,<span class="hljs-string">"is_fraud"</span>:<span class="hljs-literal">true</span>,<span class="hljs-string">"ip_address"</span>:<span class="hljs-string">"240.254.28.18"</span>,<span class="hljs-string">"location"</span>:{<span class="hljs-string">"latitude"</span>:<span class="hljs-string">"60.3920"</span>,<span class="hljs-string">"longitude"</span>:<span class="hljs-string">"11.9718"</span>},<span class="hljs-string">"device"</span>:{<span class="hljs-string">"id"</span>:<span class="hljs-string">"1e57d9d2-de48-4bf2-9131-e70ceb5f4fee"</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"mobile"</span>,<span class="hljs-string">"os"</span>:<span class="hljs-string">"macos"</span>},<span class="hljs-string">"merchant_id"</span>:<span class="hljs-number">20</span>}
...
</code></pre>
<h2 id="testing-your-fraud-detection-app">Testing Your Fraud Detection App</h2>
<p>With realistic streaming data available, you can now test your fraud detection app using the data generated by the Datagen CLI. Consume the data from the transactions Kafka topic and implement your fraud detection logic, which may involve analyzing transaction patterns, comparing with historical data, or applying machine learning models.</p>
<p>As a next step you can use Materialize to create a materialized view of the transactions data. This will allow you to query the data in real-time and build a fraud detection dashboard.</p>
<p><img src="https://user-images.githubusercontent.com/21223421/229496137-39de1a7b-2358-4364-9a16-2c4c17c31ec5.png" alt=""></p>
<h2 id="conclusion">Conclusion</h2>
<p>The Datagen CLI is a simple tool for generating realistic streaming data for testing and development purposes. In this tutorial, we showcased how to use Datagen CLI to simulate a fraud detection app use-case. With this knowledge, you can create your own schemas and generate data for various streaming data applications.</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>Useful Links:</p>
<ul>
<li><a href="https://github.com/MaterializeInc/datagen">Datagen CLI</a></li>
<li><a href="https://materialize.com/docs/">Materialize Docs</a></li>
<li><a href="https://materialize.com/s/chat">Materialize Community Slack</a></li>
<li><a href="https://github.com/MaterializeInc/demos/">Materialize Demos</a></li>
</ul>
</p>]]></description>
                                                            <category>javascript</category>
                                            <category>nodejs</category>
                                            <category>sql</category>
                                            <category>materialize</category>
                                            <category>streaming data</category>
                                            <category>redpanda</category>
                                            <category>kafka</category>
                                            <category>datagen</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/10796</guid>
                <pubDate>Mon, 03 Apr 2023 04:28:54 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Reset Local Repository Branch to be Just Like Remote Repository HEAD: A Step-by-Step Guide]]></title>
                <link>https://devdojo.com/bobbyiliev/reset-local-repository-branch-to-be-just-like-remote-repository-head-a-step-by-step-guide</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>In the world of Git, it's not uncommon for developers to find themselves in a situation where their local repository has diverged from the remote repository. This can happen due to accidental changes, conflicts, or outdated branches.</p>
<p>To get your local branch back in sync with the remote repository HEAD, you'll need to reset it. In this blog post, we'll walk you through the steps to reset your local repository branch to be just like the remote repository HEAD, making sure your Git workflow stays efficient and up-to-date.</p>
<h2 id="step-1-fetch-the-latest-changes-from-the-remote-repository">Step 1: Fetch the Latest Changes from the Remote Repository</h2>
<p>Before resetting your local branch, you'll first need to fetch the latest changes from the remote repository. To do this, open your terminal or command prompt and navigate to the root directory of your local Git repository. Then, execute the following command:</p>
<pre><code class="hljs">git fetch origin
</code></pre>
<p>This command fetches the latest changes from the remote repository (usually called "origin") without merging them into your local branch.</p>
<h2 id="step-2-reset-your-local-branch-to-the-remote-repository-head">Step 2: Reset Your Local Branch to the Remote Repository HEAD</h2>
<p>Now that you've fetched the latest changes, it's time to reset your local branch to match the remote repository HEAD. Execute the following command, replacing "branch-name" with the name of the branch you want to reset:</p>
<pre><code class="hljsperl">git <span class="hljs-keyword">reset</span> --hard origin/branch-name
</code></pre>
<p>This command resets your local branch to the specified remote branch, discarding any local changes that were made since the last commit.</p>
<h2 id="step-3-verify-your-local-branch-is-in-sync-with-the-remote-repository-head">Step 3: Verify Your Local Branch is in Sync with the Remote Repository HEAD</h2>
<p>To ensure that your local branch has been successfully reset and is now in sync with the remote repository HEAD, execute the following command:</p>
<pre><code class="hljs">git status
</code></pre>
<p>If your local branch has been successfully reset, you should see a message stating that your branch is up-to-date with the remote branch.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Resetting your local repository branch to match the remote repository HEAD is a simple but essential skill when working with Git.</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<p>By following these steps, you'll ensure that your Git workflow remains efficient and up-to-date.</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>For even more helpful Git tips and tricks, be sure to download our free <a href="https://github.com/bobbyiliev/introduction-to-git-and-github-ebook">Introduction to Git and GitHub eBook</a>!</p>
<p>Happy coding!</p>
]]></description>
                                                            <category>bash</category>
                                            <category>git</category>
                                            <category>github</category>
                                            <category>.dev</category>
                                            <category>linux</category>
                                            <category>devops</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/10781</guid>
                <pubDate>Thu, 30 Mar 2023 12:23:52 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Introduction to Rust]]></title>
                <link>https://devdojo.com/bobbyiliev/introduction-to-rust</link>
                <description><![CDATA[<h2 id="what-is-rust">What is Rust?</h2>
<p>Rust is a systems programming language. It is designed to be fast, reliable, and maintainable.</p>
<p>Rust was started in 2006 by Graydon Hoare at Mozilla Research. It was originally designed to be a safer alternative to C++. It has since become a popular language for writing low-level code, such as operating systems and device drivers. Version 1.0 was released in 2015.</p>
<p>Firefox started sponsoring Rust and Firefox Quantum was rewritten in Rust in 2017, which boosted Firefox's performance.</p>
<h2 id="installing-rust">Installing Rust</h2>
<p>To install Rust, you can use</p>
<p><a href="https://rustup.rs/">rustup</a></p>
<p>. Rustup is the official tool for installing Rust. It will install the Rust compiler and Cargo, Rust's package manager, and build tool.</p>
<pre><code class="hljsgo">curl --proto <span class="hljs-string">'=https'</span> --tlsv1<span class="hljs-number">.2</span> -sSf &lt;https:<span class="hljs-comment">//sh.rustup.rs&gt; | sh</span>
</code></pre>
<h2 id="what-is-cargo">What is Cargo?</h2>
<p>Cargo is Rust's package manager and build tool. It is used to build, test, and package Rust projects.</p>
<p>If you are coming from a JavaScript background, Cargo is similar to npm. Or if you are coming from a Python background, Cargo is similar to pip.</p>
<p>The compiler that Cargo uses is called <code>rustc</code>. You can use <code>rustc</code> to compile a single file.</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<h2 id="hello-world">Hello World</h2>
<p>To create your first Rust project, you can use Cargo's <code>new</code> command. This will create a new project with the name <code>hello_world</code>.</p>
<pre><code class="hljsgo">bash cargo <span class="hljs-built_in">new</span> hello_world
</code></pre>
<p>Output:</p>
<pre><code class="hljsgo">     Created binary (application) <span class="hljs-string">`hello_world`</span> <span class="hljs-keyword">package</span>
</code></pre>
<p>This will create a new directory called <code>hello_world</code> with the following structure:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">hello_world</span>
├── <span class="hljs-selector-tag">Cargo</span><span class="hljs-selector-class">.toml</span>
└── <span class="hljs-selector-tag">src</span>
   └── <span class="hljs-selector-tag">main</span><span class="hljs-selector-class">.rs</span>
</code></pre>
<p>The <code>Cargo.toml</code> file contains the project's metadata and is the configuration file for your project. Rust uses semantic versioning, so you can specify the version of Rust that your project requires.</p>
<p>The <code>src</code> directory contains the source code for the project.</p>
<p>The <code>main.rs</code> file is the entry point for the project.</p>
<p>To run the project, you can use the <code>run</code> command which also builds the project.</p>
<pre><code class="hljs">bash cargo run
</code></pre>
<p>Output:</p>
<pre><code class="hljsperl">   Compiling
   hello_world v<span class="hljs-number">0</span>.<span class="hljs-number">1.0</span> (hello_world)
   Finished dev [unoptimized + debuginfo] target(<span class="hljs-keyword">s</span>) in <span class="hljs-number">0</span>.<span class="hljs-number">82</span><span class="hljs-keyword">s</span>
   Running <span class="hljs-string">`target/debug/hello_world`</span>
   Hello, world!
</code></pre>
<p>The <code>target</code> directory contains the compiled project. The <code>debug</code> directory contains the unoptimized version of the project.</p>
<p>You can add <code>--release</code> to the <code>run</code> command to build the project in release mode.</p>
<pre><code class="hljs">cargo run --release
</code></pre>
<p>The <code>release</code> directory contains the optimized version of the project.</p>
<h2 id="variables">Variables</h2>
<p>To declare a variable, you can use the <code>let</code> keyword. The <code>let</code> keyword is used to create a variable that is immutable by default. This means that the variable cannot be changed once it is created this improves the safety and the performance.</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">let</span> x = <span class="hljs-number">5</span>;
</code></pre>
<p>Rust is a strongly typed language, so you must specify the type of the variable. The type of the variable is inferred from the value that is assigned to the variable.</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">let</span> x: i32 = <span class="hljs-number">5</span>;
</code></pre>
<p>You can initialize multiple variables on the same line.</p>
<pre><code class="hljsperl">let (<span class="hljs-keyword">x</span>, <span class="hljs-keyword">y</span>) = (<span class="hljs-number">1</span>, <span class="hljs-number">2</span>);
</code></pre>
<p>To initialize a mutable variable, you can use the <code>mut</code> keyword.</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">let</span> mut x = <span class="hljs-number">5</span>;
</code></pre>
<p>As in almost all programming languages, you can declare constants with the <code>const</code> keyword.</p>
<pre><code class="hljsgo"><span class="hljs-keyword">const</span> MAX_POINTS: u32 = <span class="hljs-number">100</span>_000;
</code></pre>
<p>The <code>const</code> keyword is used to create a constant that is valid for the entire time a program runs. Constants are always immutable. The convention for constants is to use all uppercase with underscores between words.</p>
<h3 id="scope">Scope</h3>
<p>Variables are scoped to the block that they are declared in. This means that variables declared in a block are only accessible in that block.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-number">1</span>;
    let <span class="hljs-keyword">y</span> = <span class="hljs-number">10</span>;
    println!(<span class="hljs-string">"x = {}, y = {}"</span>, <span class="hljs-keyword">x</span>, <span class="hljs-keyword">y</span>);
}
</code></pre>
<p>The <code>y</code> variable is only accessible in the block that it is declared in. If you try to access the <code>y</code> variable outside of the block, you will get a compiler error.</p>
<pre><code class="hljsjavascript">error[E0425]: cannot find value <span class="hljs-string">`y`</span> <span class="hljs-keyword">in</span> <span class="hljs-keyword">this</span> scope  --&gt; src/main.rs:<span class="hljs-number">6</span>:<span class="hljs-number">35</span>
| <span class="hljs-number">6</span> |     println!(<span class="hljs-string">"x = {}, y = {}"</span>, x, y);     |
                    ^ not found <span class="hljs-keyword">in</span> <span class="hljs-keyword">this</span> scope
</code></pre>
<h3 id="shadowing">Shadowing</h3>
<p>You can declare a new variable with the same name as a previous variable. This is called shadowing. The new variable shadows the previous variable. The previous variable is no longer accessible.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-number">1</span>;
    let <span class="hljs-keyword">x</span> = <span class="hljs-keyword">x</span> + <span class="hljs-number">1</span>;
    println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>The <code>x</code> variable is shadowed by the <code>x</code> variable on the second line.</p>
<p>The first <code>x</code> variable is no longer accessible.</p>
<h2 id="memory-safety">Memory Safety</h2>
<p>Rust is a memory safe language. This means that Rust prevents memory errors such as null pointer dereference, use after free, and double free.</p>
<p>Uninitialized memory is a common source of memory errors. Rust prevents uninitialized memory by requiring you to initialize all variables.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span>: i32;
    println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>If you try to use an uninitialized variable, you will get a compiler error.</p>
<pre><code class="hljsperl">error[E0381]: <span class="hljs-keyword">use</span> of possibly uninitialized variable: <span class="hljs-string">`x`</span>  --&gt; src/main.rs:<span class="hljs-number">3</span>:<span class="hljs-number">25</span>
| <span class="hljs-number">3</span> |     println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);     |
                    ^ <span class="hljs-keyword">use</span> of possibly uninitialized <span class="hljs-string">`x`</span>
</code></pre>
<h2 id="functions">Functions</h2>
<p>Functions are declared with the <code>fn</code> keyword.</p>
<p>The <code>fn</code> keyword is used to declare a function that is immutable by default. This means that the function cannot be changed once it is created.</p>
<pre><code class="hljsgo">fn hello_world() {
    <span class="hljs-built_in">println</span>!(<span class="hljs-string">"Hello, world!"</span>);
}
</code></pre>
<p>The Rust guidelines recommend using <code>snake_case</code> for function names.</p>
<p>To call a function, you can use the function name followed by parentheses.</p>
<pre><code class="hljsgo">fn hello_world() {
    <span class="hljs-built_in">println</span>!(<span class="hljs-string">"Hello, world!"</span>);
}
fn main() {
    hello_world();
}
</code></pre>
<p>Functions do not have to appear in the same order as they are called. You can call a function before it is declared.</p>
<pre><code class="hljsgo">fn main() {
    hello_world();
}
fn hello_world() {
    <span class="hljs-built_in">println</span>!(<span class="hljs-string">"Hello, world!"</span>);
}
</code></pre>
<h3 id="parameters">Parameters</h3>
<p>Functions can have parameters. Parameters are declared in the function signature.</p>
<pre><code class="hljsgo">fn hello_world(name: &amp;str) {
    <span class="hljs-built_in">println</span>!(<span class="hljs-string">"Hello, {}!"</span>, name);
}
fn main() {
    hello_world(<span class="hljs-string">"World"</span>);
}
</code></pre>
<p>The <code>&amp;str</code> type is a string slice. A string slice is a reference to a string.</p>
<p>String slices are immutable by default. This means that the string slice cannot be changed once it is created.</p>
<h3 id="return-values">Return Values</h3>
<p>Functions can return values. The return type is declared in the function signature.</p>
<pre><code class="hljsperl">fn add(<span class="hljs-keyword">x</span>: i32, <span class="hljs-keyword">y</span>: i32) -&gt; i32 {
    <span class="hljs-keyword">x</span> + <span class="hljs-keyword">y</span>
}
fn main() {
    let sum = add(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>);
    println!(<span class="hljs-string">"sum = {}"</span>, sum);
}
</code></pre>
<p>The <code>-&gt; i32</code> part of the function signature declares the return type of the function.
The <code>-&gt; i32</code> part of the function signature is optional.</p>
<p>If the function does not return a value, you can omit the <code>-&gt; i32</code> part of the function signature.</p>
<h2 id="modules-system">Modules System</h2>
<p>Rust uses a module system to organize code. Modules are declared with the <code>mod</code> keyword.</p>
<pre><code class="hljsgo">mod hello_world {
    pub fn hello_world() {
        <span class="hljs-built_in">println</span>!(<span class="hljs-string">"Hello, world!"</span>);
    }
}
fn main() {
    hello_world::hello_world();
}
</code></pre>
<p>The <code>use</code> keyword is used to bring a module into scope.</p>
<pre><code class="hljsphp">mod hello_world {
    pub fn hello_world() {
        println!(<span class="hljs-string">"Hello, world!"</span>);
    }
}
<span class="hljs-keyword">use</span> <span class="hljs-title">hello_world</span>::<span class="hljs-title">hello_world</span>;
fn main() {
    hello_world();
}
</code></pre>
<h2 id="scalar-types">Scalar Types</h2>
<p>There are 4 scalar types in Rust: integers, floating-point numbers, Booleans, and characters.</p>
<p>Types of integers:</p>
<ul>
<li>Signed integers: <code>i8</code>, <code>i16</code>, <code>i32</code>, <code>i64</code>, <code>i128</code>, <code>isize</code></li>
<li>Unsigned integers: <code>u8</code>, <code>u16</code>, <code>u32</code>, <code>u64</code>, <code>u128</code>, <code>usize</code></li>
</ul>
<p>The <code>isize</code> and <code>usize</code> types depend on the kind of computer that your program is running on: 64 bits if you’re on a 64-bit architecture and 32 bits if you’re on a 32-bit architecture.</p>
<p>The default integer type is <code>i32</code>. This is usually the fastest, even on 64-bit systems. The default integer type can be changed with the <code>#![feature(default_int_type)]</code> attribute.</p>
<pre><code class="hljsperl"><span class="hljs-comment">#![feature(default_int_type)]</span>
fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-number">1</span>;
    println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>Floating-point numbers:</p>
<ul>
<li><code>f32</code></li>
<li><code>f64</code></li>
</ul>
<p>The default floating-point type is <code>f64</code>. This type is usually the fastest, even on 32-bit systems. The default floating-point type can be changed with the <code>#![feature(default_float_type)]</code> attribute.</p>
<pre><code class="hljsperl"><span class="hljs-comment">#![feature(default_float_type)]</span>
fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-number">1.0</span>;
    println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>Booleans:</p>
<ul>
<li><code>true</code></li>
<li><code>false</code></li>
</ul>
<p>Characters are always 4 bytes in size and represent a Unicode Scalar Value. Literal characters are specified with single quotes.</p>
<p>Strings do not use the character type. Strings are a collection of characters.</p>
<h2 id="compound-types">Compound Types</h2>
<p>There are 2 compound types in Rust: tuples and arrays.</p>
<h3 id="tuples">Tuples</h3>
<p>Tuples are a collection of values of different types. Tuples are declared with parentheses.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = (<span class="hljs-number">1</span>, <span class="hljs-number">2.0</span>, <span class="hljs-string">"three"</span>);
    println!(<span class="hljs-string">"x = {:?}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>The <code>{:?}</code> format specifier is used to print the tuple.</p>
<p>You can use a dot to access a tuple element.</p>
<pre><code class="hljsgo">fn main() {
    let x = (<span class="hljs-number">1</span>, <span class="hljs-number">2.0</span>, <span class="hljs-string">"three"</span>);
    <span class="hljs-built_in">println</span>!(<span class="hljs-string">"x.0 = {}"</span>, x<span class="hljs-number">.0</span>);
    <span class="hljs-built_in">println</span>!(<span class="hljs-string">"x.1 = {}"</span>, x<span class="hljs-number">.1</span>);
    <span class="hljs-built_in">println</span>!(<span class="hljs-string">"x.2 = {}"</span>, x<span class="hljs-number">.2</span>);
}
</code></pre>
<h3 id="arrays">Arrays</h3>
<p>Arrays are a collection of values of the same type. Arrays are declared with square brackets.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
    println!(<span class="hljs-string">"x = {:?}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>The <code>{:?}</code> format specifier is used to print the array.</p>
<p>Arrays are limited to a size of 32 elements. If you need a collection of more than 32 elements, you should use a vector.</p>
<p>You can use a dot to access an array element.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
    println!(<span class="hljs-string">"x[0] = {}"</span>, <span class="hljs-keyword">x</span>[<span class="hljs-number">0</span>]);
    println!(<span class="hljs-string">"x[1] = {}"</span>, <span class="hljs-keyword">x</span>[<span class="hljs-number">1</span>]);
    println!(<span class="hljs-string">"x[2] = {}"</span>, <span class="hljs-keyword">x</span>[<span class="hljs-number">2</span>]);
}
</code></pre>
<h2 id="control-flow">Control Flow</h2>
<h3 id="if-expressions"><code>if</code> Expressions</h3>
<p>The <code>if</code> keyword is used to create an <code>if</code> expression.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-number">1</span>;
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">x</span> == <span class="hljs-number">1</span> {
        println!(<span class="hljs-string">"x is 1"</span>);
    }
}
</code></pre>
<h3 id="if-else-expressions"><code>if</code>-<code>else</code> Expressions</h3>
<p>The <code>else</code> keyword is used to create an <code>if</code>-<code>else</code> expression.</p>
<pre><code class="hljsgo">fn main() {
    let x = <span class="hljs-number">1</span>;
    <span class="hljs-keyword">if</span> x == <span class="hljs-number">1</span> {
        <span class="hljs-built_in">println</span>!(<span class="hljs-string">"x is 1"</span>);
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-built_in">println</span>!(<span class="hljs-string">"x is not 1"</span>);
    }
}
</code></pre>
<h3 id="if-else-if-else-expressions"><code>if</code>-<code>else if</code>-<code>else</code> Expressions</h3>
<p>The <code>else if</code> keyword is used to create an <code>if</code>-<code>else if</code>-<code>else</code> expression.</p>
<pre><code class="hljsgo">fn main() {
    let x = <span class="hljs-number">1</span>;
    <span class="hljs-keyword">if</span> x == <span class="hljs-number">1</span> {
        <span class="hljs-built_in">println</span>!(<span class="hljs-string">"x is 1"</span>);
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> x == <span class="hljs-number">2</span> {
        <span class="hljs-built_in">println</span>!(<span class="hljs-string">"x is 2"</span>);
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-built_in">println</span>!(<span class="hljs-string">"x is not 1 or 2"</span>);
    }
}
</code></pre>
<p>You can assign the result of an <code>if</code> expression to a variable.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-number">1</span>;
    let <span class="hljs-keyword">y</span> = <span class="hljs-keyword">if</span> <span class="hljs-keyword">x</span> == <span class="hljs-number">1</span> {
        <span class="hljs-number">2</span>
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-number">3</span>
    };
    println!(<span class="hljs-string">"y = {}"</span>, <span class="hljs-keyword">y</span>);
}
</code></pre>
<p>Note that there is a semicolon after the <code>if</code> expression when it is assigned to a variable.</p>
<p>We can't use <code>return</code> in an <code>if</code> expression.</p>
<p>There is no ternary operator in Rust. The <code>if</code> expression is the closest thing to a ternary operator.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-number">1</span>;
    let <span class="hljs-keyword">y</span> = <span class="hljs-keyword">if</span> <span class="hljs-keyword">x</span> == <span class="hljs-number">1</span> { <span class="hljs-number">2</span> } <span class="hljs-keyword">else</span> { <span class="hljs-number">3</span> };
    println!(<span class="hljs-string">"y = {}"</span>, <span class="hljs-keyword">y</span>);
}
</code></pre>
<h3 id="loop-expressions"><code>loop</code> Expressions</h3>
<p>The <code>loop</code> keyword is used to create a <code>loop</code> expression.</p>
<pre><code class="hljsgo">fn main() {
    loop {
        <span class="hljs-built_in">println</span>!(<span class="hljs-string">"Hello, world!"</span>);
    }
}
</code></pre>
<p>The <code>break</code> keyword is used to break out of a <code>loop</code> expression.</p>
<pre><code class="hljsgo">fn main() {
    loop {
        <span class="hljs-built_in">println</span>!(<span class="hljs-string">"Hello, world!"</span>);
        <span class="hljs-keyword">break</span>;
    }
}
</code></pre>
<p>To break a nested <code>loop</code> expression, you can use a label.</p>
<pre><code class="hljspowershell">fn main() {
    <span class="hljs-string">'outer: loop {
        '</span>inner: loop {
            println!(<span class="hljs-string">"Hello, world!"</span>);
            <span class="hljs-keyword">break</span> <span class="hljs-string">'outer;
        }
    }
}
</span></code></pre>
<h3 id="while-expressions"><code>while</code> Expressions</h3>
<p>The <code>while</code> keyword is used to create a <code>while</code> expression.</p>
<pre><code class="hljsperl">fn main() {
    let mut <span class="hljs-keyword">x</span> = <span class="hljs-number">1</span>;
    <span class="hljs-keyword">while</span> <span class="hljs-keyword">x</span> &lt;= <span class="hljs-number">3</span> {
        println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);
        <span class="hljs-keyword">x</span> += <span class="hljs-number">1</span>;
    }
}
</code></pre>
<p>There is no <code>do-while</code> loop in Rust. But you can use a <code>loop</code> expression with a <code>break</code> expression.</p>
<pre><code class="hljsperl">fn main() {
    let mut <span class="hljs-keyword">x</span> = <span class="hljs-number">1</span>;
    loop {
        println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);
        <span class="hljs-keyword">x</span> += <span class="hljs-number">1</span>;
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">x</span> &gt; <span class="hljs-number">3</span> {
            <span class="hljs-keyword">break</span>;
        }
    }
}
</code></pre>
<h3 id="for-expressions"><code>for</code> Expressions</h3>
<p>The <code>for</code> keyword is used to create a <code>for</code> expression.</p>
<pre><code class="hljsperl">fn main() {
    <span class="hljs-keyword">for</span> <span class="hljs-keyword">x</span> in <span class="hljs-number">1</span>..<span class="hljs-number">4</span> {
        println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);
    }
}
</code></pre>
<p>The <code>iter</code> method is used to iterate over a collection.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
    <span class="hljs-keyword">for</span> <span class="hljs-keyword">y</span> in x.iter() {
        println!(<span class="hljs-string">"y = {}"</span>, <span class="hljs-keyword">y</span>);
    }
}
</code></pre>
<p>The <code>enumerate</code> method is used to iterate over a collection and get the index of each element.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
    <span class="hljs-keyword">for</span> (i, <span class="hljs-keyword">y</span>) in x.iter().enumerate() {
        println!(<span class="hljs-string">"i = {}, y = {}"</span>, i, <span class="hljs-keyword">y</span>);
    }
}
</code></pre>
<p>The <code>for</code> can use a range.</p>
<pre><code class="hljsperl">fn main() {
    <span class="hljs-keyword">for</span> <span class="hljs-keyword">x</span> in <span class="hljs-number">1</span>..<span class="hljs-number">4</span> {
        println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);
    }
}
</code></pre>
<p>The <code>for</code> can use a range with a step.</p>
<pre><code class="hljsperl">fn main() {
    <span class="hljs-keyword">for</span> <span class="hljs-keyword">x</span> in (<span class="hljs-number">1</span>..<span class="hljs-number">4</span>).step_by(<span class="hljs-number">2</span>) {
        println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);
    }
}
</code></pre>
<h2 id="strings">Strings</h2>
<p>There are at least 6 different string types in Rust. But we will focus on the most common string types.</p>
<h3 id="string-slices">String slices</h3>
<p>String slices are a reference to a string. String slices are declared with double quotes.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-string">"Hello, world!"</span>;
    println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>A literal string always a borrowed string slice.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-string">"Hello, world!"</span>;
    let <span class="hljs-keyword">y</span> = <span class="hljs-string">"Hello, world!"</span>;
    println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);
    println!(<span class="hljs-string">"y = {}"</span>, <span class="hljs-keyword">y</span>);
    println!(<span class="hljs-string">"x == y: {}"</span>, <span class="hljs-keyword">x</span> == <span class="hljs-keyword">y</span>);
}
</code></pre>
<h3 id="string-type"><code>String</code> type</h3>
<p>The <code>String</code> type is a heap-allocated string. The <code>String</code> type is declared with the <code>String::new</code> function.</p>
<pre><code class="hljsjavascript">fn main() {
    <span class="hljs-keyword">let</span> x = <span class="hljs-built_in">String</span>::<span class="hljs-keyword">new</span>();
    println!(<span class="hljs-string">"x = {}"</span>, x);
}
</code></pre>
<p>The <code>String</code> type is declared with the <code>to_string</code> method.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-string">"Hello, world!"</span>.to_string();
    println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>The <code>String</code> type is declared with the <code>String::from</code> function.</p>
<pre><code class="hljsjavascript">fn main() {
    <span class="hljs-keyword">let</span> x = <span class="hljs-built_in">String</span>::<span class="hljs-keyword">from</span>(<span class="hljs-string">"Hello, world!"</span>);
    println!(<span class="hljs-string">"x = {}"</span>, x);
}
</code></pre>
<p>A borrowed string slice can be converted to a <code>String</code> type.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-string">"Hello, world!"</span>.to_string();
    println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<h3 id="str-type"><code>&amp;str</code> type</h3>
<p>The <code>&amp;str</code> type is a borrowed string slice. The <code>&amp;str</code> type is declared with the <code>&amp;</code> operator.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-string">"Hello, world!"</span>;
    println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>A <code>String</code> type can be converted to a borrowed string slice.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-string">"Hello, world!"</span>.to_string();
    let <span class="hljs-keyword">y</span> = &amp;<span class="hljs-keyword">x</span>;
    println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);
    println!(<span class="hljs-string">"y = {}"</span>, <span class="hljs-keyword">y</span>);
}
</code></pre>
<p>A vector of <code>String</code> types example.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-keyword">vec</span>![<span class="hljs-string">"Hello, world!"</span>.to_string(), <span class="hljs-string">"Hello, world!"</span>.to_string()];
    println!(<span class="hljs-string">"x = {:?}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<h2 id="ownership">Ownership</h2>
<p>Ownership is a Rust feature that allows you to manage memory. The Rust compiler ensures that memory is always valid.</p>
<p>Each value in Rust has a variable that's called its owner. There can only be one owner at a time. When the owner goes out of scope, the value will be dropped.</p>
<p>There is only one owner of a value. The value is moved to the new owner.</p>
<p>When the owner goes out of scope, the value will be dropped.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-number">1</span>;
    let <span class="hljs-keyword">y</span> = <span class="hljs-keyword">x</span>;
    println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>); <span class="hljs-regexp">//</span> error here
    println!(<span class="hljs-string">"y = {}"</span>, <span class="hljs-keyword">y</span>); <span class="hljs-regexp">//</span> value moved here
}
</code></pre>
<h3 id="stack-and-heap">Stack and Heap</h3>
<p>Stack:</p>
<ul>
<li>Fast access</li>
<li>Fixed size</li>
<li>Last in, first out</li>
<li>In order</li>
</ul>
<p>Heap:</p>
<ul>
<li>Slow access</li>
<li>Dynamic size</li>
<li>First in, first out</li>
<li>Out of order</li>
</ul>
<h3 id="clone">Clone</h3>
<p>The <code>clone</code> method is used to clone a value.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-number">1</span>;
    let <span class="hljs-keyword">y</span> = x.clone();
    println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);
    println!(<span class="hljs-string">"y = {}"</span>, <span class="hljs-keyword">y</span>);
}
</code></pre>
<h3 id="references-and-borrowing">References and Borrowing</h3>
<p>References allow you to refer to some value without taking ownership of it.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-number">1</span>;
    let <span class="hljs-keyword">y</span> = &amp;<span class="hljs-keyword">x</span>;
    println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);
    println!(<span class="hljs-string">"y = {}"</span>, <span class="hljs-keyword">y</span>);
}
</code></pre>
<p>The <code>&amp;</code> operator is used to create a reference.</p>
<p>Under the hood, the <code>&amp;</code> operator is a <code>borrow</code> method.</p>
<p>References are immutable by default. The <code>&amp;mut</code> operator is used to create a mutable reference.</p>
<pre><code class="hljsperl">fn main() {
    let mut <span class="hljs-keyword">x</span> = <span class="hljs-number">1</span>;
    let <span class="hljs-keyword">y</span> = &amp;mut <span class="hljs-keyword">x</span>;
    *<span class="hljs-keyword">y</span> += <span class="hljs-number">1</span>;
    println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);
    println!(<span class="hljs-string">"y = {}"</span>, <span class="hljs-keyword">y</span>);
}
</code></pre>
<p>To dereference a reference, use the <code>*</code> operator.</p>
<pre><code class="hljsperl">fn main() {
    let mut <span class="hljs-keyword">x</span> = <span class="hljs-number">1</span>;
    let <span class="hljs-keyword">y</span> = &amp;mut <span class="hljs-keyword">x</span>;
    *<span class="hljs-keyword">y</span> += <span class="hljs-number">1</span>;
    println!(<span class="hljs-string">"x = {}"</span>, <span class="hljs-keyword">x</span>);
    println!(<span class="hljs-string">"y = {}"</span>, <span class="hljs-keyword">y</span>);
}
</code></pre>
<h3 id="structs">Structs</h3>
<p>A struct is a custom data type that you can name and shape however you want.</p>
<pre><code class="hljsperl">struct Point {
    <span class="hljs-keyword">x</span>: i32,
    <span class="hljs-keyword">y</span>: i32,
}

Note that there is a comma after the <span class="hljs-keyword">last</span> field.

Instanciating a struct.

<span class="hljs-string">``</span><span class="hljs-string">`rs
fn main() {
    let x = Point { x: 1, y: 2 };
    println!("x = {:?}", x);
}
</span></code></pre>
<p>An associated function is a function that is associated with a struct.</p>
<pre><code class="hljsperl">impl Point {
    fn new(<span class="hljs-keyword">x</span>: i32, <span class="hljs-keyword">y</span>: i32) -&gt; Self {
        Self { <span class="hljs-keyword">x</span>, <span class="hljs-keyword">y</span> }
    }
}

fn main() {
    let <span class="hljs-keyword">x</span> = Point::new(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>);
    println!(<span class="hljs-string">"x = {:?}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>The <code>Self</code> type is an alias for the type that the <code>impl</code> block is for.</p>
<h3 id="methods">Methods</h3>
<p>Methods are functions that are associated with a struct.</p>
<pre><code class="hljsphp">impl Point {
    fn <span class="hljs-keyword">new</span>(x: i32, y: i32) -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">Self</span> { x, y }
    }

    fn distance(&amp;<span class="hljs-keyword">self</span>) -&gt; f64 {
        ((<span class="hljs-keyword">self</span>.x.pow(<span class="hljs-number">2</span>) + <span class="hljs-keyword">self</span>.y.pow(<span class="hljs-number">2</span>)) <span class="hljs-keyword">as</span> f64).sqrt()
    }
}

fn main() {
    let x = Point::new(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>);
    println!(<span class="hljs-string">"x = {:?}"</span>, x);
    println!(<span class="hljs-string">"x.distance() = {}"</span>, x.distance());
}
</code></pre>
<h3 id="traits">Traits</h3>
<p>Traits are similar to interfaces in other languages.</p>
<pre><code class="hljsphp"><span class="hljs-keyword">trait</span> HasArea {
    fn area(&amp;<span class="hljs-keyword">self</span>) -&gt; f64;
}

impl HasArea <span class="hljs-keyword">for</span> Point {
    fn area(&amp;<span class="hljs-keyword">self</span>) -&gt; f64 {
        <span class="hljs-number">0.0</span>
    }
}

fn main() {
    let x = Point::new(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>);
    println!(<span class="hljs-string">"x = {:?}"</span>, x);
    println!(<span class="hljs-string">"x.area() = {}"</span>, x.area());
}
</code></pre>
<p>Rundown of the code above:</p>
<ul>
<li>The <code>HasArea</code> trait is defined.</li>
<li>The <code>HasArea</code> trait is implemented for the <code>Point</code> struct.</li>
<li>The <code>area</code> method is defined.</li>
<li>The <code>area</code> method is implemented for the <code>Point</code> struct.</li>
</ul>
<p>A hands on example of traits.</p>
<pre><code class="hljsphp"><span class="hljs-keyword">trait</span> Car {
    fn <span class="hljs-keyword">new</span>() -&gt; <span class="hljs-keyword">Self</span>;
    fn drive(&amp;<span class="hljs-keyword">self</span>);
}

struct Toyota {
    model: String,
}

impl Car <span class="hljs-keyword">for</span> Toyota {
    fn <span class="hljs-keyword">new</span>() -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">Self</span> {
            model: <span class="hljs-string">"Corolla"</span>.to_string(),
        }
    }

    fn drive(&amp;<span class="hljs-keyword">self</span>) {
        println!(<span class="hljs-string">"The {} is driving."</span>, <span class="hljs-keyword">self</span>.model);
    }
}

fn main() {
    let x = Toyota::new();
    x.drive();
}
</code></pre>
<h3 id="copy">Copy</h3>
<p>The <code>Copy</code> trait is used to indicate that a type can be copied.</p>
<pre><code class="hljsperl"><span class="hljs-comment">#[derive(Copy, Clone)]</span>
struct Point {
    <span class="hljs-keyword">x</span>: i32,
    <span class="hljs-keyword">y</span>: i32,
}

fn main() {
    let <span class="hljs-keyword">x</span> = Point { <span class="hljs-keyword">x</span>: <span class="hljs-number">1</span>, <span class="hljs-keyword">y</span>: <span class="hljs-number">2</span> };
    let <span class="hljs-keyword">y</span> = <span class="hljs-keyword">x</span>;
    println!(<span class="hljs-string">"x = {:?}"</span>, <span class="hljs-keyword">x</span>);
    println!(<span class="hljs-string">"y = {:?}"</span>, <span class="hljs-keyword">y</span>);
}
</code></pre>
<h3 id="debug">Debug</h3>
<p>The <code>Debug</code> trait is used to indicate that a type can be printed.</p>
<pre><code class="hljsperl"><span class="hljs-comment">#[derive(Debug)]</span>
struct Point {
    <span class="hljs-keyword">x</span>: i32,
    <span class="hljs-keyword">y</span>: i32,
}

fn main() {
    let <span class="hljs-keyword">x</span> = Point { <span class="hljs-keyword">x</span>: <span class="hljs-number">1</span>, <span class="hljs-keyword">y</span>: <span class="hljs-number">2</span> };
    println!(<span class="hljs-string">"x = {:?}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<h2 id="collections">Collections</h2>
<p>Collections are data structures that can group multiple values into a single type.</p>
<h3 id="vectors">Vectors</h3>
<p>Vectors are resizable arrays. They can grow or shrink in size.</p>
<pre><code class="hljsperl">fn main() {
    let mut <span class="hljs-keyword">x</span> = <span class="hljs-keyword">vec</span>![<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
    x.push(<span class="hljs-number">4</span>);
    println!(<span class="hljs-string">"x = {:?}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>The <code>push</code> and <code>pop</code> methods are used to add and remove elements from the end of a vector.</p>
<pre><code class="hljsperl">fn main() {
    let mut <span class="hljs-keyword">x</span> = <span class="hljs-keyword">vec</span>![<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
    x.push(<span class="hljs-number">4</span>);
    x.pop();
    println!(<span class="hljs-string">"x = {:?}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>The <code>!vec</code> macro is used to create a vector.</p>
<h3 id="hash-maps">Hash Maps</h3>
<p>Hash maps are key-value stores. In some languages, they are also known as dictionaries or associative arrays.</p>
<pre><code class="hljsgo">fn main() {
    let mut x = HashMap::<span class="hljs-built_in">new</span>();
    x.insert(<span class="hljs-string">"one"</span>, <span class="hljs-number">1</span>);
    x.insert(<span class="hljs-string">"two"</span>, <span class="hljs-number">2</span>);
    <span class="hljs-built_in">println</span>!(<span class="hljs-string">"x = {:?}"</span>, x);
}
</code></pre>
<p>Common methods on hash maps:</p>
<ul>
<li><code>insert</code>: Insert a key-value pair into a hash map.</li>
<li><code>get</code>: Get the value associated with a key.</li>
<li><code>remove</code>: Remove a key-value pair from a hash map.</li>
<li><code>len</code>: Get the number of key-value pairs in a hash map.</li>
</ul>
<pre><code class="hljsgo">fn main() {
    let mut x = HashMap::<span class="hljs-built_in">new</span>();
    x.insert(<span class="hljs-string">"one"</span>, <span class="hljs-number">1</span>);
    x.insert(<span class="hljs-string">"two"</span>, <span class="hljs-number">2</span>);
    <span class="hljs-built_in">println</span>!(<span class="hljs-string">"x = {:?}"</span>, x);
    <span class="hljs-built_in">println</span>!(<span class="hljs-string">"x.get(\"one\") = {:?}"</span>, x.get(<span class="hljs-string">"one"</span>));
    x.remove(<span class="hljs-string">"one"</span>);
    <span class="hljs-built_in">println</span>!(<span class="hljs-string">"x = {:?}"</span>, x);
    <span class="hljs-built_in">println</span>!(<span class="hljs-string">"x.len() = {:?}"</span>, x.<span class="hljs-built_in">len</span>());
}
</code></pre>
<h2 id="enums">Enums</h2>
<p>Enums are types that have a few definite values.</p>
<pre><code class="hljsperl">enum Color {
    Red,
    Green,
    Blue,
}

fn main() {
    let <span class="hljs-keyword">x</span> = Color::Red;
    println!(<span class="hljs-string">"x = {:?}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>Enums can also have data associated with them.</p>
<pre><code class="hljsperl">enum Color {
    Red,
    Green,
    Blue,
    RgbColor(u8, u8, u8),
    CmykColor { cyan: u8, magenta: u8, yellow: u8, black: u8 },
}

fn main() {
    let <span class="hljs-keyword">x</span> = Color::RgbColor(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
    println!(<span class="hljs-string">"x = {:?}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<h3 id="option">Option</h3>
<p>The <code>Option</code> enum is used to indicate that a value may or may not be present.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = Some(<span class="hljs-number">1</span>);
    println!(<span class="hljs-string">"x = {:?}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>The <code>match</code> expression is used to handle the different cases of an enum.</p>
<pre><code class="hljsgo">fn main() {
    let x = Some(<span class="hljs-number">1</span>);
    match x {
        Some(i) =&gt; <span class="hljs-built_in">println</span>!(<span class="hljs-string">"x = {:?}"</span>, i),
        None =&gt; <span class="hljs-built_in">println</span>!(<span class="hljs-string">"x is empty"</span>),
    }
}
</code></pre>
<p>To create a <code>None</code> value, use the <code>None</code> keyword.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span>: Option&lt;i32&gt; = None;
    println!(<span class="hljs-string">"x = {:?}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<h3 id="result">Result</h3>
<p>The <code>Result</code> enum is used to indicate that a function may or may not succeed.</p>
<pre><code class="hljspowershell"><span class="hljs-comment">#[must_use]</span>
<span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Result</span>&lt;<span class="hljs-title">T</span>, <span class="hljs-title">E</span>&gt;</span> {
    Ok(T),
    Err(E),
}
</code></pre>
<p>The <code>Result</code> enum has two variants: <code>Ok</code> and <code>Err</code>. The <code>Ok</code> variant indicates that the function succeeded. The <code>Err</code> variant indicates that the function failed.</p>
<pre><code class="hljsperl"><span class="hljs-keyword">use</span> std::fs::File;

fn main() {
    let <span class="hljs-keyword">x</span> = File::<span class="hljs-keyword">open</span>(<span class="hljs-string">"hello.txt"</span>);
    println!(<span class="hljs-string">"x = {:?}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>You can <code>unwrap</code> a <code>Result</code> to get the value inside the <code>Ok</code> variant.</p>
<pre><code class="hljsperl"><span class="hljs-keyword">use</span> std::fs::File;

fn main() {
    let <span class="hljs-keyword">x</span> = File::<span class="hljs-keyword">open</span>(<span class="hljs-string">"hello.txt"</span>).unwrap();
    println!(<span class="hljs-string">"x = {:?}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>You can also <code>expect</code> a <code>Result</code> to get the value inside the <code>Ok</code> variant.</p>
<pre><code class="hljsperl"><span class="hljs-keyword">use</span> std::fs::File;

fn main() {
    let <span class="hljs-keyword">x</span> = File::<span class="hljs-keyword">open</span>(<span class="hljs-string">"hello.txt"</span>).expect(<span class="hljs-string">"Failed to open hello.txt"</span>);
    println!(<span class="hljs-string">"x = {:?}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<h2 id="closures">Closures</h2>
<p>A closure is a function that can capture its environment.</p>
<p>The type and the arguments of a closure are defined in the same way as a function.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = |a: i32, b: i32| -&gt; i32 { a + b };
    println!(<span class="hljs-string">"x(1, 2) = {}"</span>, <span class="hljs-keyword">x</span>(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>));
}
</code></pre>
<p>A closure will borrow a reference:</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-number">1</span>;
    let <span class="hljs-keyword">y</span> = |a: i32| -&gt; i32 { a + <span class="hljs-keyword">x</span> };
    println!(<span class="hljs-string">"y(2) = {}"</span>, <span class="hljs-keyword">y</span>(<span class="hljs-number">2</span>));
}
</code></pre>
<p>Here is an example on how to use a closure to sort a vector.</p>
<pre><code class="hljsperl">fn main() {
    let mut <span class="hljs-keyword">x</span> = <span class="hljs-keyword">vec</span>![<span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">2</span>];
    x.sort_by(|a, b| a.cmp(b));
    println!(<span class="hljs-string">"x = {:?}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>Rundown of the code above:</p>
<ul>
<li>We create a vector with the values <code>1</code>, <code>3</code>, and <code>2</code>.</li>
<li>We sort the vector using the <code>sort_by</code> method.</li>
<li>The <code>sort_by</code> method takes a closure as an argument.</li>
<li>The closure takes two arguments: <code>a</code> and <code>b</code>.</li>
<li>The closure returns a <code>std::cmp::Ordering</code> value.</li>
<li>The <code>cmp</code> method compares two values and returns an <code>Ordering</code> value.</li>
<li>The <code>sort_by</code> method sorts the vector based on the <code>Ordering</code> value returned by the closure.</li>
</ul>
<h2 id="threads">Threads</h2>
<p>Threads are used to run multiple tasks simultaneously.</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">use</span> <span class="hljs-selector-tag">std</span><span class="hljs-selector-pseudo">::thread</span>;

<span class="hljs-selector-tag">fn</span> <span class="hljs-selector-tag">main</span>() {
    <span class="hljs-attribute">thread</span>::<span class="hljs-built_in">spawn</span>(|| {
        println!(<span class="hljs-string">"Hello from a thread!"</span>);
    });
    <span class="hljs-selector-tag">println</span>!("<span class="hljs-selector-tag">Hello</span> <span class="hljs-selector-tag">from</span> <span class="hljs-selector-tag">the</span> <span class="hljs-selector-tag">main</span> <span class="hljs-selector-tag">thread</span>!");
}
</code></pre>
<p>The <code>thread::spawn</code> function takes a closure as an argument and returns a <code>JoinHandle</code>. A <code>JoinHandle</code> is a handle to a thread that can be used to wait for the thread to finish.</p>
<p>The more threads you have, the more context-switching you have to do. This can slow down your program if you have too many threads.</p>
<h2 id="conclusion">Conclusion</h2>
<p>This is the end of the Rust tutorial. I hope you enjoyed it. If you have any questions, feel free to ask them in the <a href="/questions">community forum</a>.</p>
<p>As the next steps, I recommend you to:</p>
<ul>
<li>Read the <a href="https://doc.rust-lang.org/book/">Rust book</a>.</li>
<li>Read the <a href="https://doc.rust-lang.org/reference/">Rust reference</a>.</li>
<li>Read the <a href="https://doc.rust-lang.org/rust-by-example/">Rust by example</a>.</li>
<li>Read the <a href="https://doc.rust-lang.org/nomicon/">Rustonomicon</a>.</li>
</ul>
</p>]]></description>
                                                            <category> coding</category>
                                            <category>rust</category>
                                            <category>cargo</category>
                                            <category>rustc</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/10755</guid>
                <pubDate>Fri, 24 Mar 2023 09:17:30 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to increase the size of the file upload in Laravel Voyager Media Manager?]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-increase-the-size-of-the-file-upload-in-laravel-voyager-media-manager</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Laravel Voyager is a great open-source admin panel for Laravel. It is also the admin panel currently used in <a href="https://devdojo.com/wave">Laravel Wave</a> which is a Laravel SaaS starter kit.</p>
<p>In this article, you will learn how to increase the size of the file upload in Laravel Voyager Media Manager.</p>
<h2 id="php-changes">PHP changes</h2>
<p>First, you will need to increase the size of the file upload in the PHP configuration file. You can do this by editing the <code>php.ini</code> file and changing the <code>upload_max_filesize</code> and <code>post_max_size</code> values.</p>
<p>If you don't know where your <code>php.ini</code> file is located, you can create a new file called <code>info.php</code> and add the following code:</p>
<pre><code class="hljsxml"><span class="php"><span class="hljs-meta">&lt;?php</span>
phpinfo();
</span></code></pre>
<p>Then, you can access the <code>info.php</code> file in your browser and it will show you the location of your <code>php.ini</code> file.</p>
<p>After you have increased the size of the file upload in the PHP configuration file, you will need to restart your web server. And then visit the <code>info.php</code> file again to make sure that the changes have been applied.</p>
<h2 id="dropzone-js-changes">Dropzone.js changes</h2>
<p>The Laravel Voyager Media Manager uses the <a href="https://www.dropzone.dev/">Dropzone.js</a> library to handle the file upload. So, you will need to increase the size of the file upload in the Dropzone definition.</p>
<p>The default size of the file upload in the Laravel Voyager Media Manager is 256MB.</p>
<p>For this, we will copy the media manager view file from the Laravel Voyager package to our project. You can do this by running the following command:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<ul>
<li>Create the <code>resources/views/vendor/voyager/media</code> directory if it doesn't exist:</li>
</ul>
<pre><code class="hljsperl"><span class="hljs-keyword">mkdir</span> -p resources/views/vendor/voyager/media
</code></pre>
<ul>
<li>Copy the <code>manager.blade.php</code> file from the Laravel Voyager package to our project:</li>
</ul>
<pre><code class="hljs">cp vendor/tcg/voyager/resources/views/media/manager.blade.php resources/views/vendor/voyager/media/manager.blade.php
</code></pre>
<p>Now, you can open the <code>resources/views/vendor/voyager/media/manager.blade.php</code> file and set the <code>maxFilesize</code> value to the desired size.</p>
<ul>
<li>Open the file and go to line <code>847</code>:</li>
</ul>
<pre><code class="hljs">nano +847 resources/views/vendor/voyager/media/manager.blade.php
</code></pre>
<p>And inside the <code>dropzone</code> function add a new line and set the <code>maxFilesize</code> value to the desired size:</p>
<pre><code class="hljsgo">...
            dropzone.dropzone({
                <span class="hljs-comment">// Add the line below</span>
                maxFilesize: <span class="hljs-number">500</span>,
                timeout: <span class="hljs-number">180000</span>,
                url: <span class="hljs-string">'{{ route('</span>voyager.media.upload<span class="hljs-string">') }}'</span>,
...
</code></pre>
<p>Save the file and you are done. After that go to the media manager page and do a hard refresh to make sure that the cache is cleared and try to upload a file that is larger than the default size.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In this article, you have learned how to increase the size of the file upload in Laravel Voyager Media Manager.</p>
<p>If you are building a SaaS application, you can use the <a href="https://devdojo.com/wave">Laravel Wave</a> starter kit which will let you build a SaaS application quickly as it comes with a lot of features out of the box!</p>
<p>If you have any questions, feel free to ask them in the community forum at <a href="https://devdojo.com/questions">https://devdojo.com/forum</a>.</p>
]]></description>
                                                            <category>javascript</category>
                                            <category>php</category>
                                            <category>laravel</category>
                                            <category>voyager</category>
                                            <category>wave</category>
                                            <category>blade</category>
                                            <category>dropzone</category>
                                            <category>upload limit</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/10343</guid>
                <pubDate>Thu, 05 Jan 2023 01:11:44 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to find a process locking port 3000 on Mac?]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-find-a-process-locking-port-3000-on-mac</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Are you having trouble with a process locking port <code>3000</code> (or essentially any other port) on your Mac? Here's how you can find and kill the process!</p>
<h2 id="finding-the-process">Finding the process</h2>
<p>To find the process, first open the Terminal app on your Mac.</p>
<p>You can find it in the Utilities folder within your Applications folder.</p>
<p>Then ype the following command and press Enter:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">sudo</span> <span class="hljs-selector-tag">lsof</span> <span class="hljs-selector-tag">-i</span> <span class="hljs-selector-pseudo">:3000</span>
</code></pre>
<p>This command will list all the processes that are currently using port <code>3000</code>. The output will include the process ID (<code>PID</code>), the user that owns the process, and the name of the process.</p>
<h2 id="kill-the-process">Kill the process</h2>
<p>Once you've identified the process that you want to kill and note down its PID.</p>
<p>To kill the process, use the following command:</p>
<pre><code class="hljsxml">kill -9 <span class="hljs-tag">&lt;<span class="hljs-name">PID</span>&gt;</span>
</code></pre>
<blockquote>
<p>Note: Replace <code>&lt;PID&gt;</code> with the actual <code>PID</code> of the process.</p>
</blockquote>
<p><strong>The <code>-9</code> option kills the process immediately, without giving it the opportunity to clean up after itself. This can potentially cause issues. As an alternative, you may want to consider using the <code>-15</code> (<code>TERM</code>) or <code>-3</code> (<code>QUIT</code>) option for a gentler termination, which allows the process to perform any necessary cleanup before it is terminated.</strong></p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<h2 id="conclusion">Conclusion</h2>
<p>If the process was successfully killed, you should no longer see it listed when you run the lsof command again.</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>I hope this helps! Let me know if you have any questions.</p>
]]></description>
                                                            <category>javascript</category>
                                            <category>terminal</category>
                                            <category>nodejs</category>
                                            <category>linux</category>
                                            <category>commands</category>
                                            <category>mac</category>
                                            <category>devops</category>
                                            <category>port</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/10296</guid>
                <pubDate>Wed, 21 Dec 2022 03:17:37 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to setup Laravel Wave on Windows using Laragon?]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-setup-laravel-wave-on-windows-using-laragon</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Laravel is a free, open-source PHP web framework, created by Taylor Otwell and intended for the development of web applications following the model–view–controller (MVC) architectural pattern and based on Symfony. Some of the features of Laravel are a modular packaging system with a dedicated dependency manager, different ways for accessing relational databases, utilities that aid in application deployment and maintenance, and its orientation toward syntactic sugar.</p>
<p>Laravel Wave is a SaaS starter-kit built with Laravel and Tailwind CSS. It is a fully functional SaaS application that you can use to build your own SaaS application. It comes with a lot of features that you can use to build your own SaaS application.</p>
<p>Laragon is a Windows application that allows you to run multiple PHP applications on your computer. Laragon is one of the best solutions for running Laravel on Windows!</p>
<p>In this tutorial, we will be installing Wave on Laragon.</p>
<h2 id="download-laragon">Download Laragon</h2>
<p>Visit <a href="https://laragon.org/download/">https://laragon.org/download/</a> and download the full Laragon version as it comes with PHP 8.x.</p>
<p>Then run the Laragon exe file and follow the steps.</p>
<h2 id="laragon-installation-steps">Laragon Installation Steps</h2>
<p>Once you run the Laragon exe file, follow the installation guide and make changes based on your needs:</p>
<ul>
<li><strong>Select destination location</strong>: using the default <code>C:\laragon</code> is sufficient, but feel free to change that according to your personal preferences.</li>
<li>Feel free to disable ‘Run Laragon when Windows starts’</li>
<li>Enable the ‘Auto virtual hosts’ feature</li>
<li>Finally, click <code>Install</code> and wait for the installation to complete</li>
</ul>
<p>After the installation, restart your computer.</p>
<p>After the restart, you should see the Laragon icon in your system tray. Click on it and then click on <code>Start All Services</code>:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<p><img src="https://i.imgur.com/oj1R34r.png" alt=""></p>
<h2 id="create-a-new-laravel-project">Create a new Laravel project</h2>
<p>After the Laragon installation is complete, create a new Laravel project by following these steps:</p>
<ul>
<li>Click on the <code>Menu</code> button</li>
<li>Click on <code>Quick app</code></li>
<li>Select <code>Blank</code></li>
<li>Choose a name for the project</li>
<li>And click on <code>OK</code></li>
</ul>
<p>Then click on the <code>Terminal</code> icon and <code>cd</code> into the <code>C:\laragon\www\wave</code> directory and clone the Wave project:</p>
<pre><code class="hljsphp">git <span class="hljs-keyword">clone</span> https:<span class="hljs-comment">//github.com/thedevdojo/wave.git .</span>
</code></pre>
<blockquote>
<p>Note: make sure to change the <code>thedevdojo</code> username with your repo where you've cloned the Wave project</p>
</blockquote>
<p><img src="https://imgur.com/P3WpInM.png" alt=""></p>
<p>Next, change the document root to the <code>public</code> folder of the project:</p>
<ul>
<li>Click on the <code>Menu</code> button</li>
<li>Click on <code>Apache</code></li>
<li>Click on <code>sites-enabled</code></li>
<li>Click on the <code>wave.test</code> file</li>
<li>Change the <code>DocumentRoot</code> to <code>C:\laragon\www\wave\public</code> and the <code>Directory</code> to <code>C:\laragon\www\wave\public</code></li>
<li>Click on <code>Save</code></li>
</ul>
<p><img src="https://imgur.com/W252MGw.png" alt=""></p>
<p>After that, click on the <code>Menu</code> button and then click on <code>Apache</code> and then click on <code>Reload Apache</code> to apply the changes.</p>
<h2 id="enable-php-sodium">Enable PHP Sodium</h2>
<p>The PHP sodium extension is not enabled by default. To enable it, follow these steps:</p>
<ul>
<li>Click on the <code>Menu</code> button</li>
<li>Click on <code>PHP</code></li>
<li>Click on <code>PHP Extensions</code></li>
<li>Enable the <code>sodium</code> extension</li>
<li>Click on <code>Save</code></li>
</ul>
<h2 id="installing-wave">Installing Wave</h2>
<p>After the Laragon installation is complete, clone the Wave project into the <code>C:\laragon\www\wave</code> directory or the one location that you’ve installed Laragon in. You can do that by clicking the <code>Terminal</code> icon and then running the following command:</p>
<pre><code class="hljs">cd C:\laragon\www\wave
</code></pre>
<p>Then install the dependencies:</p>
<pre><code class="hljs">composer install --ignore-platform-req=ext-redis
</code></pre>
<h2 id="update-your-database-details">Update your database details</h2>
<p>After the installation, update your <code>.env</code> file and add the necessary database details. First copy the <code>.env.example</code> file and rename it to <code>.env</code>:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">copy</span> <span class="hljs-selector-class">.env</span><span class="hljs-selector-class">.example</span> <span class="hljs-selector-class">.env</span>
</code></pre>
<p>Then open the <code>.env</code> file and update the database details.</p>
<p>The default details for Laragon should be:</p>
<pre><code class="hljs">DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=wave
DB_USERNAME=root
DB_PASSWORD=
</code></pre>
<blockquote>
<p>Note: change the <code>DB_DATABASE=wave</code> with the name of your project, eg. use the same name as you did when you created the project in Laragon.</p>
</blockquote>
<p>While you are in the <code>.env</code> file, you can also update the <code>APP_URL</code> to match your local URL. For example, if you are using the default Laragon URL, you can update it to:</p>
<pre><code class="hljsgo">APP_URL=http:<span class="hljs-comment">//wave.test</span>
</code></pre>
<p>Then run the Laravel migrations:</p>
<pre><code class="hljs">php artisan migrate
</code></pre>
<p>Then run the Laravel seeders:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">php</span> <span class="hljs-selector-tag">artisan</span> <span class="hljs-selector-tag">db</span><span class="hljs-selector-pseudo">:seed</span>
</code></pre>
<p>Then visit <a href="http://wave.test">http://wave.test</a> in your browser and you should see the Wave application!</p>
<h2 id="conclusion">Conclusion</h2>
<p>Wave is just a standard Laravel application, so you can use any of the Laravel deployment methods to deploy it. In this tutorial, we’ve used Laragon to install Wave. You can also use Laragon to install other Laravel applications.</p>
<p>For more information about Laragon, visit <a href="https://laragon.org/">https://laragon.org/</a>.</p>
<p>For more information about Wave, visit <a href="https://wave.devdojo.com/">https://wave.devdojo.com/</a>.</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>If you have any questions, post them on the DevDojo community forum <a href="https://devdojo.com/questions">https://devdojo.com/questions</a>.</p>
]]></description>
                                                            <category>php</category>
                                            <category>laravel</category>
                                            <category>wave</category>
                                            <category>windows</category>
                                            <category>saas</category>
                                            <category>laravel wave</category>
                                            <category>laragon</category>
                                            <category>wamp</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/10268</guid>
                <pubDate>Sat, 10 Dec 2022 06:14:31 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to show all tables in PostgreSQL?]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-show-all-tables-in-postgresql</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>PostgreSQL is a powerful, open-source database system. It is a relational database management system (RDBMS) based on the SQL language. PostgreSQL is one of the most popular database systems in the world. It is the go-to database for many developers and companies.</p>
<p>In this tutorial, we will show you how to list all tables in PostgreSQL using the <code>psql</code> command-line tool.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>To follow along with this tutorial, you will need:</p>
<ul>
<li>A PostgreSQL database server installed on your computer. You can download and install PostgreSQL from the <a href="https://www.postgresql.org/download/">official website</a>. Or you can use a cloud-based PostgreSQL database such as DigitalOcean's <a href="https://www.digitalocean.com/products/managed-databases-postgresql/">managed PostgreSQL</a>.</li>
<li><code>psql</code> command-line tool installed on your computer.</li>
</ul>
<h2 id="step-1-connect-to-the-postgresql-database">Step 1 — Connect to the PostgreSQL database</h2>
<p>To connect to the PostgreSQL database, run the following command:</p>
<pre><code class="hljs">psql -U postgres -h localhost -p 5432
</code></pre>
<ul>
<li>The <code>-U</code> flag specifies the username to connect to the database. In this case, we are connecting to the default <code>postgres</code> user.</li>
<li>The <code>-h</code> flag specifies the hostname of the database server. In this case, we are connecting to the database server running on the same computer, so we use <code>localhost</code>.</li>
<li>The <code>-p</code> flag specifies the port number of the database server. In this case, we are connecting to the default port number <code>5432</code>.</li>
</ul>
<h2 id="step-2-switch-to-a-specific-database">Step 2 — Switch to a specific database</h2>
<p>To check the list of all databases, run the following command:</p>
<pre><code class="hljs">\l
</code></pre>
<p>That is the equivalent of running <code>SHOW DATABASES;</code> in MySQL.</p>
<p>To switch to a specific database, run the following command:</p>
<pre><code class="hljs">\c database_name
</code></pre>
<ul>
<li>The <code>\c</code> command is used to switch to a specific database.</li>
</ul>
<p>The <code>\c</code> command is similar to the <code>USE</code> command in MySQL.</p>
<h2 id="step-3-list-all-tables-in-the-database">Step 3 — List all tables in the database</h2>
<p>To list all tables in the database, run the following command:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<pre><code class="hljs">\dt
</code></pre>
<ul>
<li>The <code>\dt</code> command is used to list all tables in the database in the public schema.</li>
</ul>
<p>The <code>\dt</code> command is similar to the <code>SHOW TABLES;</code> command in MySQL.</p>
<p>To list all tables in the database in all schemas, run the following command:</p>
<pre><code class="hljsmarkdown">\dt <span class="hljs-emphasis">*.*</span>
</code></pre>
<ul>
<li>The <code>*.*</code> specifies that we want to list all tables in all schemas.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>In this tutorial, you learned how to list all tables in PostgreSQL using the <code>psql</code> command-line tool.</p>
<p>To learn more about SQL, check out this free SQL eBook:</p>
<blockquote>
<p><a href="https://github.com/bobbyiliev/introduction-to-sql">Introduction to SQL eBook</a></p>
</blockquote>
<p>As a next step, I would recommend checking out Materialize. It is a streaming database that makes it easy to build real-time applications. It is the perfect database for building data-driven applications:</p>
<blockquote>
<p><a href="https://materialize.com?utm_source=bobbyiliev">Materialize</a></p>
</blockquote>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
]]></description>
                                                            <category>database</category>
                                            <category>sql</category>
                                            <category>postgres</category>
                                            <category>postgresql</category>
                                            <category>materialize</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/10168</guid>
                <pubDate>Mon, 07 Nov 2022 04:39:41 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to flatten JSON array in SQL using jsonb_array_elements?]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-flatten-json-array-in-sql-using-jsonb-array-elements</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>The <code>jsonb_array_elements</code> function returns a set of rows, one for each element of the input array. The input array can be a top-level array or a nested array.</p>
<p>In this tutorial, we will learn how to flatten JSON array in SQL using <code>jsonb_array_elements</code>.</p>
<h2 id="jsonb-array-elements-example"><code>jsonb_array_elements</code> Example</h2>
<p>Let's start by creating a table with a JSON column:</p>
<pre><code class="hljspowershell">CREATE TABLE users (
    id INT,
    name TEXT,
    <span class="hljs-keyword">data</span> JSONB
);
</code></pre>
<p>Next, let's insert some data into the table:</p>
<pre><code class="hljsruby">INSERT INTO users VALUES
    (<span class="hljs-number">1</span>, <span class="hljs-string">'John'</span>, <span class="hljs-string">'{"age": 30, "hobbies": ["gaming", "coding"]}'</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:JSONB</span>),
    (<span class="hljs-number">2</span>, <span class="hljs-string">'Jane'</span>, <span class="hljs-string">'{"age": 25, "hobbies": ["reading", "cooking"]}'</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:JSONB</span>);
</code></pre>
<p>After that query the table as normal:</p>
<pre><code class="hljs">SELECT * FROM users;
</code></pre>
<p>Output:</p>
<pre><code class="hljsruby"> id <span class="hljs-params">| name |</span>          data
----+------+-----------------------
  <span class="hljs-number">1</span> <span class="hljs-params">| John |</span> {<span class="hljs-string">"age"</span>: <span class="hljs-number">30</span>, <span class="hljs-string">"hobbies"</span>: [<span class="hljs-string">"gaming"</span>, <span class="hljs-string">"coding"</span>]}
  <span class="hljs-number">2</span> <span class="hljs-params">| Jane |</span> {<span class="hljs-string">"age"</span>: <span class="hljs-number">25</span>, <span class="hljs-string">"hobbies"</span>: [<span class="hljs-string">"reading"</span>, <span class="hljs-string">"cooking"</span>]}
(<span class="hljs-number">2</span> rows)
</code></pre>
<p>Let's use the <code>jsonb_array_elements</code> function to flatten the <code>hobbies</code> array:</p>
<pre><code class="hljsphp">SELECT
    id,
    name,
    data-&gt;&gt;<span class="hljs-string">'age'</span> <span class="hljs-keyword">AS</span> age,
    hobby
FROM users, jsonb_array_elements(data-&gt;<span class="hljs-string">'hobbies'</span>) hobby;
</code></pre>
<p>Output:</p>
<pre><code class="hljsruby"> id <span class="hljs-params">| name |</span> age <span class="hljs-params">| hobby
----+------+-----+-------
  1 |</span> John <span class="hljs-params">|  30 |</span> gaming
  <span class="hljs-number">1</span> <span class="hljs-params">| John |</span>  <span class="hljs-number">30</span> <span class="hljs-params">| coding
  2 |</span> Jane <span class="hljs-params">|  25 |</span> reading
  <span class="hljs-number">2</span> <span class="hljs-params">| Jane |</span>  <span class="hljs-number">25</span> <span class="hljs-params">| cooking
(4 rows)
</span></code></pre>
<h2 id="creating-a-materialized-view">Creating a <a href="https://materialize.com/docs/sql/create-materialized-view/">Materialized View</a></h2>
<p>Now that we have our query, let's create a materialized view with the <code>jsonb_array_elements</code> function to store the flattened data:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<pre><code class="hljsphp">CREATE MATERIALIZED VIEW users_hobbies <span class="hljs-keyword">AS</span>
    SELECT
        id,
        name,
        data-&gt;&gt;<span class="hljs-string">'age'</span> <span class="hljs-keyword">AS</span> age,
        hobby
    FROM users, jsonb_array_elements(data-&gt;<span class="hljs-string">'hobbies'</span>) hobby;
</code></pre>
<p>Let's query the materialized view:</p>
<pre><code class="hljs">SELECT * FROM users_hobbies;
</code></pre>
<p>Output:</p>
<pre><code class="hljsruby"> id <span class="hljs-params">| name |</span> age <span class="hljs-params">| hobby
----+------+-----+-------
  1 |</span> John <span class="hljs-params">|  30 |</span> gaming
  <span class="hljs-number">1</span> <span class="hljs-params">| John |</span>  <span class="hljs-number">30</span> <span class="hljs-params">| coding
  2 |</span> Jane <span class="hljs-params">|  25 |</span> reading
  <span class="hljs-number">2</span> <span class="hljs-params">| Jane |</span>  <span class="hljs-number">25</span> <span class="hljs-params">| cooking
</span></code></pre>
<p>With PostgreSQL, when creating a materialized view, your query is executed and the results are stored in the materialized view. This means that the materialized view is not updated when the underlying table is updated. To update the materialized view, you need to run the <a href="https://www.postgresql.org/docs/current/sql-refreshmaterializedview.html"><code>REFRESH MATERIALIZED VIEW</code></a> command.</p>
<p>If you want to have a live materialized view, that is incrementally updated in milliseconds when the underlying table is updated, you can use Materialize. Materialize is a streaming SQL database that supports incremental updates. You can learn more about Materialize <a href="https://materialize.com?utm_source=bobbyiliev">here</a>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>This tutorial showed you how to flatten JSON array in SQL using <code>jsonb_array_elements</code>.</p>
<p>To learn more about SQL in general, check out this free SQL eBook:</p>
<blockquote>
<p><a href="https://sql.bobby.sh/">Free Introduction to SQL eBook</a></p>
</blockquote>
<p>To learn more about Materialize, check out the official documentation:</p>
<blockquote>
<p><a href="https://materialize.com/docs/">Materialize Documentation</a></p>
</blockquote>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
]]></description>
                                                            <category>database</category>
                                            <category>json</category>
                                            <category>sql</category>
                                            <category>postgres</category>
                                            <category>materialize</category>
                                            <category>jsonb</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/10097</guid>
                <pubDate>Wed, 19 Oct 2022 05:07:45 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[9 Open Source Projects to Contribute to - Hacktoberfest 2022]]></title>
                <link>https://devdojo.com/bobbyiliev/9-open-source-projects-to-contribute-to-hacktoberfest-2022</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Hacktoberfest is a month-long event where people are awarded for contributing to open source projects 🙌.</p>
<p>In order to participate you need to sign up <a href="https://hacktoberfest.digitalocean.com/">here</a> and then Submit four pull requests (PRs) to a GitHub repo tagged with the <code>hacktoberfest</code> label and redeem a Hacktoberfest shirt.</p>
<p>For more information, you can find the official rules <a href="https://hacktoberfest.digitalocean.com/resources">here</a>.</p>
<h3 id="how-to-submit-your-first-pull-request-on-github">How to Submit Your First Pull Request on GitHub</h3>
<p>If you are new to Git and GitHub, make sure to go through this tutorial here on how to submit your first pull request:</p>
<p><a href="https://www.digitalocean.com/community/tutorials/hacktoberfest-how-to-submit-your-first-pull-request-on-github">How to Submit Your First Pull Request on GitHub</a></p>
<p>Also, you can watch this Git and GitHub crash course on YouTube here:</p>
<p><a href="https://www.youtube.com/watch?v=UpdhourKi3c&amp;list=PLY7SzAmnEqp7P6KHmBKBVNIjZkXMKh_fn&amp;ab_channel=BobbyIliev">Introduction to Git and GitHub video series</a></p>
<p>Or you can also read this free eBook here:</p>
<p><a href="https://github.com/bobbyiliev/introduction-to-git-and-github-ebook">Introduction to Git and GitHub eBook</a></p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<p>Once you have the necessary Git and GitHub knowledge you are ready to start hacking!</p>
<p>Here is a list with a few open-source projects that I maintain which you can contribute to!</p>
<h2 id="tailwindcss-components">TailwindCSS Components</h2>
<p>The Tails Open Source Components are a (no-config) copy'n paste free collection of hand-crafted templates and components built in TailwindCSS:</p>
<p></p><div class="flex"><svg class="mr-3 w-10 h-10" fill="currentColor" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd" /></svg>
            <a href="https://github.com/thedevdojo/tails" target="_blank">thedevdojo/tails</a></div>
<h2 id="larasail">LaraSail</h2>
<p>LaraSail is a CLI tool, written in Bash, for Laravel to help you do all of the necessary initial server setup on DigitalOcean:</p>
<p></p><div class="flex"><svg class="mr-3 w-10 h-10" fill="currentColor" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd" /></svg>
            <a href="https://github.com/thedevdojo/larasail" target="_blank">thedevdojo/larasail</a></div>
<h2 id="laravel-wave">Laravel Wave</h2>
<p>Wave is a Software as a Service Starter Kit that can help you build your next great idea 💰. Wave is built with Laravel, Voyager, TailwindCSS, and a few other awesome technologies. Here are some of the awesome features ✨:</p>
<p></p><div class="flex"><svg class="mr-3 w-10 h-10" fill="currentColor" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd" /></svg>
            <a href="https://github.com/thedevdojo/wave" target="_blank">thedevdojo/wave</a></div>
<h2 id="docker-ebook">Docker eBook</h2>
<p>This is an open-source introduction to Docker guide that will help you learn the basics of Docker and how to start using containers for your SysOps, DevOps, and Dev projects. No matter if you are a DevOps/SysOps engineer, developer, or just a Linux enthusiast, you will most likely have to use Docker at some point in your career:</p>
<p></p><div class="flex"><svg class="mr-3 w-10 h-10" fill="currentColor" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd" /></svg>
            <a href="https://github.com/bobbyiliev/introduction-to-docker-ebook" target="_blank">bobbyiliev/introduction-to-docker-ebook</a></div>
<h2 id="git-and-github-ebook">Git and GitHub eBook</h2>
<p>This is an open-source introduction to Git and GitHub guide that will help you learn the basics of version control and start using Git for your SysOps, DevOps, and Dev projects. No matter if you are a DevOps/SysOps engineer, developer, or just a Linux enthusiast, you can use Git to track your code changes and collaborate with other members of your team or open source maintainers:</p>
<p></p><div class="flex"><svg class="mr-3 w-10 h-10" fill="currentColor" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd" /></svg>
            <a href="https://github.com/bobbyiliev/introduction-to-git-and-github-ebook" target="_blank">bobbyiliev/introduction-to-git-and-github-ebook</a></div>
<h2 id="sql-basics-ebook">SQL Basics eBook</h2>
<p>This is an open-source introduction to SQL guide that will help you learn the basics of SQL and start using relational databases for your SysOps, DevOps, and Dev projects. No matter if you are a DevOps/SysOps engineer, developer, or just a Linux enthusiast, you will most likely have to use SQL at some point in your career:</p>
<p></p><div class="flex"><svg class="mr-3 w-10 h-10" fill="currentColor" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd" /></svg>
            <a href="https://github.com/bobbyiliev/introduction-to-sql" target="_blank">bobbyiliev/introduction-to-sql</a></div>
<h2 id="bash-scripting-ebook">Bash Scripting eBook</h2>
<p>This is an open-source introduction to Bash scripting guide/ebook that will help you learn the basics of Bash scripting and start writing awesome Bash scripts that will help you automate your daily SysOps, DevOps, and Dev tasks. No matter if you are a DevOps/SysOps engineer, developer, or just a Linux enthusiast, you can use Bash scripts to combine different Linux commands and automate boring and repetitive daily tasks, so that you can focus on more productive and fun things.</p>
<p></p><div class="flex"><svg class="mr-3 w-10 h-10" fill="currentColor" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd" /></svg>
            <a href="https://github.com/bobbyiliev/introduction-to-bash-scripting" target="_blank">bobbyiliev/introduction-to-bash-scripting</a></div>
<h2 id="laravel-tips-ebook">Laravel Tips eBook</h2>
<p>This is an open-source Laravel Tips and Tricks eBook that is a collection of my own notes that I've put together for myself throughout the years. You would more likely than not need many of those tips at some point in your career as a Laravel Developer:</p>
<p></p><div class="flex"><svg class="mr-3 w-10 h-10" fill="currentColor" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd" /></svg>
            <a href="https://github.com/bobbyiliev/laravel-tips-and-tricks-ebook" target="_blank">bobbyiliev/laravel-tips-and-tricks-ebook</a></div>
<h2 id="toc-101-linux-commands-ebook">101 Linux commands eBook</h2>
<p>This is an open-source eBook with 101 Linux commands that everyone should know.</p>
<p>This is a brand new eBook that I have been planning to write.</p>
<p>I have added a list of commands but feel free to add new suggestions too!</p>
<p></p><div class="flex"><svg class="mr-3 w-10 h-10" fill="currentColor" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd" /></svg>
            <a href="https://github.com/bobbyiliev/101-linux-commands-ebook" target="_blank">bobbyiliev/101-linux-commands-ebook</a></div>
<h2 id="conclusion">Conclusion</h2>
<p>If you have any questions make sure to check out the FAQ page <a href="https://hacktoberfest.com/participation/">here</a> or reach out to me on Twitter: <a href="https://twitter.com/bobbyiliev_">@bobbyiliev_</a></p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>If you are a project maintainer share your project in the comments below! 🙌</p>
]]></description>
                                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/9995</guid>
                <pubDate>Sun, 02 Oct 2022 11:43:13 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to find the version of an installed npm package?]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-find-the-version-of-an-installed-npm-package</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p><code>npm</code> is a package manager for Node.js. It is used to install and manage packages for your JavaScript projects. <code>npm</code> is open source and was initially released in 2009. The <code>npm</code> repository is a collection of open-source packages and tools for Node.js.</p>
<h2 id="check-npm-version">Check <code>npm</code> version</h2>
<p>Let's quickly check the version of <code>npm</code> that we are using:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">npm</span> <span class="hljs-selector-tag">-v</span>

# <span class="hljs-selector-tag">Output</span>:
8<span class="hljs-selector-class">.11</span><span class="hljs-selector-class">.0</span>
</code></pre>
<p>If you don't see the version number, you need to install <code>npm</code> first:</p>
<p><a href="https://docs.npmjs.com/downloading-and-installing-node-js-and-npm">Downloading and installing Node.js and npm</a></p>
<h2 id="check-the-version-of-the-installed-npm-packages">Check the version of the installed <code>npm</code> packages</h2>
<p>Once you have installed <code>npm</code>, you can check the versions of the installed packages for your project:</p>
<pre><code class="hljsphp">npm <span class="hljs-keyword">list</span>
</code></pre>
<p>The output will look something like this depending on the installed packages:</p>
<pre><code class="hljsperl">├── adonis-preset-ts@2.<span class="hljs-number">1.0</span>
├── luxon@2.<span class="hljs-number">1.1</span>
├── pg@8.<span class="hljs-number">7.1</span>
├── pino-pretty@7.<span class="hljs-number">2.0</span>
├── proxy-addr@2.<span class="hljs-number">0</span>.<span class="hljs-number">7</span>
├── reflect-metadata@0.<span class="hljs-number">1.13</span>
├── source-<span class="hljs-keyword">map</span>-support@0.<span class="hljs-number">5.20</span>
├── typescript@4.<span class="hljs-number">2.4</span>
├── youch-terminal@1.<span class="hljs-number">1.1</span>
</code></pre>
<h2 id="check-the-version-of-the-installed-npm-package-globally">Check the version of the installed <code>npm</code> package globally</h2>
<p>The <code>-g</code> flag is used to check the global packages. The output will be the same as the output of the <code>npm list</code> command, except that it will only show the global packages.</p>
<pre><code class="hljsphp">npm <span class="hljs-keyword">list</span> -g
</code></pre>
<p>Example output:</p>
<pre><code class="hljsruby">├── @cloudflare/wrangler@1.<span class="hljs-number">19.8</span>
├── @stackbit/cli@0.<span class="hljs-number">2.30</span>
├── npm@8.<span class="hljs-number">12.1</span>
├── pg@8.<span class="hljs-number">7.3</span>
└── typescript@4.<span class="hljs-number">6.4</span>
</code></pre>
<h2 id="check-the-versions-of-the-dependencies-of-your-packages">Check the versions of the dependencies of your packages</h2>
<p>If you want to check not only the versions of your packages but also their dependencies, you can use the <code>--depth</code> option:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<pre><code class="hljsphp">npm <span class="hljs-keyword">list</span> --depth=<span class="hljs-number">1</span>
</code></pre>
<p>This will show you the versions of your packages and their dependencies.</p>
<h2 id="check-the-version-of-a-specific-package">Check the version of a specific package</h2>
<p>You can also check the version of a specific package by adding the package name to the command:</p>
<pre><code class="hljsphp">npm <span class="hljs-keyword">list</span> pg
</code></pre>
<p>Output:</p>
<pre><code class="hljsruby">└─┬ pg@8.<span class="hljs-number">7.1</span>
  └─┬ pg-pool@3.<span class="hljs-number">4.1</span>
    └── pg@8.<span class="hljs-number">7.1</span> deduped
</code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>Using the <code>npm list</code> command is a good way to check the versions of the installed packages.</p>
<p>For more information on what the latest version of a package is, you can use the <code>npm info &lt;package&gt;</code> command.</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p>For more information, you can visit the official documentation at <a href="https://docs.npmjs.com/">docs.npmjs.com</a>.</p>
]]></description>
                                                            <category>javascript</category>
                                            <category>nodejs</category>
                                            <category>node</category>
                                            <category>npm</category>
                                            <category>webdev</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/9533</guid>
                <pubDate>Tue, 09 Aug 2022 02:44:10 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to pass command line arguments to a Node.js app?]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-pass-command-line-arguments-to-a-nodejs-app</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Similar to a Bash script where you can pass arguments to a script using the <code>$1</code> syntax, you can also pass arguments to a Node.js app.</p>
<p>In this quick tutorial, you will learn how to pass arguments to a Node.js app using the <a href="https://nodejs.org/docs/latest/api/process.html#process_process_argv"><code>process.argv</code> array</a>.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>Before you get started, you will need to have Node.js installed:</p>
<ul>
<li><a href="https://nodejs.org/en/download/">Download Node.js</a></li>
</ul>
<h2 id="pass-arguments-to-a-node-js-app">Pass arguments to a Node.js app</h2>
<p>Let's start by creating a new file called <code>script.js</code> and adding the following code to it:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">const</span> process = <span class="hljs-built_in">require</span>(<span class="hljs-string">'process'</span>);

<span class="hljs-built_in">console</span>.log(process.argv[<span class="hljs-number">2</span>]);
</code></pre>
<p>A quick rundown of the <code>process.argv</code> array:</p>
<ul>
<li><code>process.argv[0]</code> is the path to the Node.js executable</li>
<li><code>process.argv[1]</code> is the path to the script file</li>
<li><code>process.argv[2]</code> is the first argument passed to the script</li>
<li><code>process.argv[3]</code> is the second argument passed to the script and so on</li>
</ul>
<p>Let's run the script with the following command:</p>
<pre><code class="hljsmarkdown">node script.js DevDojo

<span class="hljs-section"># Output:</span>
DevDojo
</code></pre>
<h3 id="printing-all-arguments">Printing all arguments</h3>
<p>To print all arguments, you can use a <code>forEach</code> loop just as you would with a standard array:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">const</span> process = <span class="hljs-built_in">require</span>(<span class="hljs-string">'process'</span>);

process.argv.forEach(<span class="hljs-function">(<span class="hljs-params">val, index</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`<span class="hljs-subst">${index}</span>: <span class="hljs-subst">${val}</span>`</span>);
});
</code></pre>
<p>Let's run the script with the following command:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">node</span> <span class="hljs-selector-tag">script</span><span class="hljs-selector-class">.js</span> <span class="hljs-selector-tag">hi</span> <span class="hljs-selector-tag">there</span> <span class="hljs-selector-tag">devs</span>
</code></pre>
<p>We are now passing 3 arguments to the script and in this case, the output of this script will be:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<pre><code class="hljsruby"><span class="hljs-number">0</span>: <span class="hljs-regexp">/opt/homebrew</span><span class="hljs-regexp">/Cellar/node</span>@16/<span class="hljs-number">16.16</span>.<span class="hljs-number">0</span>/bin/node
<span class="hljs-number">1</span>: <span class="hljs-regexp">/Users/bobby</span><span class="hljs-regexp">/dev/script</span>.js
<span class="hljs-number">2</span>: hi
<span class="hljs-number">3</span>: there
<span class="hljs-number">4</span>: devs
</code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>This is pretty much it! I hope that you find this useful!</p>
<p>In case you are new to Node.js I could suggest the following tutorial on how to get started:</p>
<p><a href="https://devdojo.com/bo-iliev/how-to-write-your-first-nodejs-script">How To Write Your First Node.js Script</a></p>
<p>To learn more about arguments in Bash scripts, you can read the following article:</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
<p><a href="https://devdojo.com/guide/bash">Bash Scripting Tutorial</a></p>
]]></description>
                                                            <category>javascript</category>
                                            <category>nodejs</category>
                                            <category>linux</category>
                                            <category>webdev</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/9408</guid>
                <pubDate>Sat, 23 Jul 2022 12:42:08 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Quick introduction to WebSockets with Node.js]]></title>
                <link>https://devdojo.com/bobbyiliev/quick-introduction-to-websockets-with-nodejs</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>WebSockets allow you to send and receive data over a network without having to use a traditional HTTP protocol. Using WebSockets, you can build a real-time application. For example, you can send messages to other users without having to refresh the page to see the new messages.</p>
<p>We will be using the <code>ws</code> library. It is a Node.js module that allows you to create WebSockets servers and clients.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>Before you get started, make sure you have Node.js installed:</p>
<ul>
<li><a href="https://nodejs.org/en/download/">Download Node.js</a></li>
</ul>
<h2 id="setup">Setup</h2>
<p>In order to install the <code>ws</code> library you need to run the following command:</p>
<pre><code class="hljs">npm install ws
</code></pre>
<p>Next, create a new file called <code>server.js</code> and open it in your favorite text editor.</p>
<h2 id="creating-a-websocket-server">Creating a WebSocket server</h2>
<p>In the <code>server.js</code> file, create the following code:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">const</span> WebSocket = <span class="hljs-built_in">require</span>(<span class="hljs-string">'ws'</span>)

<span class="hljs-keyword">const</span> wss = <span class="hljs-keyword">new</span> WebSocket.Server({ <span class="hljs-attr">port</span>: <span class="hljs-number">8081</span> })

wss.on(<span class="hljs-string">'connection'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">connection</span>(<span class="hljs-params">ws</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Client connected'</span>)
    <span class="hljs-keyword">const</span> interval = setInterval(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
        ws.send(<span class="hljs-string">'hello world'</span>)
    }, <span class="hljs-number">1000</span>)
    ws.on(<span class="hljs-string">"close"</span>, () =&gt; {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Client disconnected"</span>);
    });
    ws.onerror = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Some Error occurred"</span>);
    }
});
</code></pre>
<p>Rundown of the code:</p>
<ul>
<li><code>const WebSocket = require('ws')</code>: Import the the <code>ws</code> module.</li>
<li><code>const wss = new WebSocket.Server({ port: 8081 })</code>: Create a new WebSocket server on port 8081.</li>
<li><code>wss.on('connection', function connection(ws) {</code>: When a new client connects to the server, the <code>connection</code> event is emitted.</li>
<li><code>const interval = setInterval(() =&gt; {</code>: Create a new interval that sends a message to the client every second.</li>
<li><code>ws.send('hello world')</code>: Send a message to the client.</li>
<li><code>ws.on("close", () =&gt; {</code>: When the client disconnects, the <code>close</code> event is emitted.</li>
<li><code>ws.onerror = function () {</code>: When an error occurs, the <code>onerror</code> event is emitted.</li>
</ul>
<p>That way we will simulate a streaming application where the server sends a message to the client every second.</p>
<p>Then to start the server, run the following command:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<pre><code class="hljscss"><span class="hljs-selector-tag">node</span> <span class="hljs-selector-tag">server</span><span class="hljs-selector-class">.js</span>
</code></pre>
<p>Next, leave the server running and open a new terminal window where we will prepare our client.</p>
<h2 id="creating-a-websocket-client">Creating a WebSocket client</h2>
<p>In a new file called <code>client.js</code> file, add the following code:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">const</span> WebSocket = <span class="hljs-built_in">require</span>(<span class="hljs-string">'ws'</span>)
<span class="hljs-keyword">const</span> ws = <span class="hljs-keyword">new</span> WebSocket(<span class="hljs-string">'ws://localhost:8081'</span>)
ws.on(<span class="hljs-string">'open'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">open</span>(<span class="hljs-params"></span>) </span>{
    ws.on(<span class="hljs-string">'message'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">message</span>(<span class="hljs-params">data</span>) </span>{
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`<span class="hljs-subst">${data}</span>`</span>);
    });
});
</code></pre>
<p>The above code creates a new WebSocket client and connects to the server on port 8081.</p>
<p>When a message is received from the server, the <code>message</code> event is emitted.</p>
<p>To test the client, run the following command:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">node</span> <span class="hljs-selector-tag">client</span><span class="hljs-selector-class">.js</span>
</code></pre>
<p>You should see the following output:</p>
<pre><code class="hljs">hello world
hello world
hello world
...
</code></pre>
<p>Example:</p>
<p><img src="https://user-images.githubusercontent.com/21223421/180609383-2ba61adc-f95d-4545-b31f-9cba81f4a3e4.gif" alt="node ws example gif"></p>
<h2 id="conclusion">Conclusion</h2>
<p>This is a simple example of how to use WebSockets with Node.js.</p>
<p>If you want to learn more about WebSockets, check out the <a href="https://github.com/websockets/ws">official documentation</a>.</p>
<p>If you want to see how WebSockets work with Laravel, check out the following tutorial:</p>
<p><a href="https://devdojo.com/bobbyiliev/how-to-use-laravel-websockets">Laravel WebSockets</a></p>
<p>For further reading, I could also suggest taking a look at the following tutorial on what the difference between SSE and WebSockets is:</p>
<p><a href="https://devdojo.com/bobbyiliev/how-to-create-a-simple-event-streaming-in-laravel#sse-vs-websockets">SSE vs WebSockets</a></p>
<p>If you are working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
]]></description>
                                                            <category>javascript</category>
                                            <category>nodejs</category>
                                            <category>node</category>
                                            <category>websockets</category>
                                            <category>webdev</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/9405</guid>
                <pubDate>Sat, 23 Jul 2022 07:30:09 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Building a live chart with Deno, WebSockets, Chart.js and Materialize]]></title>
                <link>https://devdojo.com/bobbyiliev/building-a-live-chart-with-deno-websockets-chartjs-and-materialize</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<blockquote>
<p>❗️ This demo includes examples for an unsupported version of <a href="https://materialize.com/docs/lts/">Materialize (0.26.x)</a> ❗️</p>
</blockquote>
<p>This is a self-contained example of a real-time chart powered by Deno, Web Sockets, Chart.js, and <a href="https://materialize.com?utm_source=bobbyiliev">Materialize</a>.</p>
<p><a href="https://deno.land/">Deno</a> is a simple and secure runtime for JavaScript and TypeScript that uses V8. Deno, just like Materialize, is also written in Rust.</p>
<p>In this demo, we will build a simple live dashboard app that displays real-time data from a Deno Web Socket server. Deno will then connect to Materialize and <a href="https://materialize.com/docs/sql/tail/">TAIL</a> our live materialized view to get the latest data and display it in a real-time chart using Chart.js.</p>
<h2 id="overview">Overview</h2>
<p>Here is a quick overview of the project:</p>
<ul>
<li>A mock service to continually generate user score events.</li>
<li>Redpanda instance to store the user score events in a topic.</li>
<li>Materialize instance that is connected to the Redpanda instance and ingests the data from the topic in a live materialized view which we can query in real-time using just SQL.</li>
<li>A Deno backend service that connects to Materialize and TAIL the live materialized view to get the latest data and display it in a real-time chart.</li>
<li>Frontend service that connects to the Deno app via a web socket and displays the data in a real-time chart using Chart.js.</li>
</ul>
<p>Here is a diagram of the project:</p>
<p><img src="https://user-images.githubusercontent.com/21223421/172839011-a19476ca-a156-4a06-9148-915088f125a5.png" alt="Materialize + Deno + Chart.js + Web Sockets"></p>
<h2 id="prerequisites">Prerequisites</h2>
<p>To run this demo, you need to have the following installed.</p>
<ul>
<li><a href="https://docs.docker.com/get-docker/">Install Docker</a>.</li>
<li><a href="https://docs.docker.com/compose/install/">Install Docker Compose</a>.</li>
</ul>
<h2 id="running-the-demo">Running the demo</h2>
<p>To get started, clone the repository:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<pre><code class="hljsphp">git <span class="hljs-keyword">clone</span> git <span class="hljs-keyword">clone</span> https:<span class="hljs-comment">//github.com/bobbyiliev/materialize-tutorials.git</span>
</code></pre>
<p>Then you can access the directory:</p>
<pre><code class="hljs">cd materialize-tutorials
git checkout lts
cd mz-deno-live-dashboard
</code></pre>
<p>With that you can then build the images:</p>
<pre><code class="hljs">docker-compose build
</code></pre>
<p>And finally, you can run all the containers:</p>
<pre><code class="hljs">docker-compose up -d
</code></pre>
<p>It might take a couple of minutes to start the containers and generate the demo data.</p>
<p>After that, you can visit <code>http://localhost</code> in your browser to see the demo:</p>
<p><img src="https://user-images.githubusercontent.com/21223421/172840971-8e1091ac-8ec9-4773-b414-b1e858b0c278.gif" alt="Deno websockets and chart.js"></p>
<p>Next, let's review the Materialize setup and the Deno backend setup.</p>
<h2 id="materialize-setup">Materialize setup</h2>
<p>The Deno service will execute the following DDL statements on boot so that we don't have to run them manually:</p>
<ul>
<li>Create a Kafka source: Creating a source in Materialize does not actually start the data ingestion. You can think of a non-materialized source as just the metadata needed for Materialize to connect to your source but not process any data:</li>
</ul>
<pre><code class="hljsgo">CREATE SOURCE score
FROM KAFKA BROKER <span class="hljs-string">'redpanda:9092'</span> TOPIC <span class="hljs-string">'score_topic'</span>
FORMAT BYTES;
</code></pre>
<ul>
<li>Create a <a href="https://materialize.com/docs/sql/create-view/">create non-materialized view</a>, that essentially only provides us with an alias for the <code>SELECT</code> statements they include:</li>
</ul>
<pre><code class="hljsphp">CREATE VIEW score_view <span class="hljs-keyword">AS</span>
    SELECT
        *
    FROM (
        SELECT
            (data-&gt;&gt;<span class="hljs-string">'user_id'</span>)::int <span class="hljs-keyword">AS</span> user_id,
            (data-&gt;&gt;<span class="hljs-string">'score'</span>)::int <span class="hljs-keyword">AS</span> score,
            (data-&gt;&gt;<span class="hljs-string">'created_at'</span>)::double <span class="hljs-keyword">AS</span> created_at
        FROM (
            SELECT CAST(data <span class="hljs-keyword">AS</span> jsonb) <span class="hljs-keyword">AS</span> data
            FROM (
                SELECT convert_from(data, <span class="hljs-string">'utf8'</span>) <span class="hljs-keyword">AS</span> data
                FROM score
            )
        )
    );
</code></pre>
<ul>
<li>Create a materialized view:</li>
</ul>
<pre><code class="hljsphp">CREATE MATERIALIZED VIEW score_view_mz <span class="hljs-keyword">AS</span>
    SELECT
        (SUM(score))::int <span class="hljs-keyword">AS</span> user_score,
        user_id
    FROM score_view GROUP BY user_id;
</code></pre>
<p>To check if the views and the sources were created, launch the Materialize CLI:</p>
<pre><code class="hljs">docker-compose run mzcli
</code></pre>
<blockquote>
<p>This is just a shortcut to a docker container with postgres-client pre-installed, if you already have <code>psql</code> you could run <code>psql -U materialize -h localhost -p 6875 materialize</code>.</p>
</blockquote>
<p>Then check the views and the sources:</p>
<pre><code class="hljsruby">SHOW VIEWS;
-- <span class="hljs-symbol">Output:</span>
-- +-----------------+
-- <span class="hljs-params">| score_view      |</span>
-- <span class="hljs-params">| score_view_mz   |</span>
-- +-----------------+

SHOW sources;
-- <span class="hljs-symbol">Output:</span>
-- +-----------------+
-- <span class="hljs-params">| score           |</span>
-- +-----------------+
</code></pre>
<h3 id="using-tail">Using <code>TAIL</code></h3>
<p>Next, to see the results in real-time we can use <code>TAIL</code>:</p>
<pre><code class="hljs">COPY ( TAIL score_view_mz ) TO STDOUT;
</code></pre>
<p>You will see a flow of the new user score that was generated in real-time.</p>
<p>We can also start a <code>TAIL</code> without a snapshot, which means that you will only see the latest records after the query is run:</p>
<pre><code class="hljsgo">COPY ( TAIL score_view_mz WITH (SNAPSHOT = <span class="hljs-literal">false</span>) ) TO STDOUT;
</code></pre>
<p>This is what we will use in our Deno application to get the top user scores and display them in a real-time chart.</p>
<p>For more information on how the <code>TAIL</code> function works, see the <a href="https://materialize.com/docs/sql/tail/">Materialize documentation</a>.</p>
<h2 id="deno">Deno</h2>
<p>Now that we have Materialize ready, let's review the Deno setup.</p>
<p>We would use two Deno modules:</p>
<ul>
<li>The Postgres module to connect to Materialize.</li>
<li>The Web Sockets module to create a Web Socket connection to our Frontend service.</li>
</ul>
<p>You can find the code in the <a href="https://github.com/bobbyiliev/materialize-tutorials/tree/main/mz-deno-live-dashboard/backend"><code>backend</code> directory</a>.</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">import</span> { WebSocketClient, WebSocketServer } <span class="hljs-keyword">from</span> <span class="hljs-string">"https://deno.land/x/websocket@v0.1.4/mod.ts"</span>;
<span class="hljs-keyword">import</span> { Client } <span class="hljs-keyword">from</span> <span class="hljs-string">"https://deno.land/x/postgres/mod.ts"</span>;

<span class="hljs-comment">// Specify your Materialize connection details</span>
<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> Client({
  <span class="hljs-attr">user</span>: <span class="hljs-string">"materialize"</span>,
  <span class="hljs-attr">database</span>: <span class="hljs-string">"materialize"</span>,
  <span class="hljs-attr">hostname</span>: <span class="hljs-string">"materialized"</span>,
  <span class="hljs-attr">port</span>: <span class="hljs-number">6875</span>,
});

<span class="hljs-keyword">await</span> client.connect();
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Connected to Postgres"</span>);

<span class="hljs-comment">// Start a transaction</span>
<span class="hljs-keyword">await</span> client.queryObject(<span class="hljs-string">'BEGIN'</span>);
<span class="hljs-comment">// Declare a cursor without a snapshot</span>
<span class="hljs-keyword">await</span> client.queryObject(<span class="hljs-string">`DECLARE c CURSOR FOR TAIL score_view_mz WITH (SNAPSHOT = false)`</span>);

<span class="hljs-keyword">const</span> wss = <span class="hljs-keyword">new</span> WebSocketServer(<span class="hljs-number">8080</span>);

wss.on(<span class="hljs-string">"connection"</span>, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">ws: WebSocketClient</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Client connected"</span>);
  setInterval(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> client.queryObject&lt;{ <span class="hljs-attr">mz_timestamp</span>: string; mz_diff: number, <span class="hljs-attr">user_id</span>: number, <span class="hljs-attr">user_score</span>: number}&gt;(<span class="hljs-string">`FETCH ALL c`</span>);
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> row <span class="hljs-keyword">of</span> result.rows) {
      <span class="hljs-keyword">let</span> message = { <span class="hljs-attr">user_id</span>: row.user_id, <span class="hljs-attr">user_score</span>: row.user_score };
      broadcastEvent(message);
    }
  } , <span class="hljs-number">1000</span>);

});

<span class="hljs-comment">// Broadcast a message to all clients</span>
<span class="hljs-keyword">const</span> broadcastEvent = <span class="hljs-function">(<span class="hljs-params">message: any</span>) =&gt;</span> {
  wss.clients.forEach(<span class="hljs-function">(<span class="hljs-params">ws: WebSocketClient</span>) =&gt;</span> {
    ws.send(<span class="hljs-built_in">JSON</span>.stringify(message));
  });
}
</code></pre>
<p>Rundown of the code:</p>
<ul>
<li>As Materialize is Postgres wire compatible, first we import the <code>Client</code> class from the <code>https://deno.land/x/postgres/mod.ts</code> module. This is the class that we will use to connect to the Materialize instance.</li>
<li>Next, we create a new <code>Client</code> instance and pass it the credentials for Materialize.</li>
<li>Then we call the <code>connect()</code> method on the client instance to connect to Materialize.</li>
<li>Next, we call the <code>queryObject()</code> method on the client instance to start a transaction and also call the <code>queryObject()</code> method on the client instance to declare a cursor without a snapshot.</li>
<li>Finally, we create a new <code>WebSocketServer</code> instance and pass it the port to listen on.</li>
<li>We then define a <code>connection</code> event handler on the <code>WebSocketServer</code> instance, which is called when a client connects.</li>
<li>We then set an interval to fetch the latest data from Materialize and broadcast it to all clients.</li>
</ul>
<h2 id="frontend-setup">Frontend setup</h2>
<p>For the frontend, we will not be using any JavaScript framework, but just the <a href="https://www.chartjs.org/">Chart.js library</a>.</p>
<p>Thanks to the web sockets connection, we can now receive the latest data from Materialize and display it in a real-time chart.</p>
<pre><code class="hljsxml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/chart.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-full mt-10"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">canvas</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"myChart"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
      <span class="hljs-keyword">const</span> ctx = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"myChart"</span>);
      <span class="hljs-keyword">const</span> myChart = <span class="hljs-keyword">new</span> Chart(ctx, {
        <span class="hljs-attr">type</span>: <span class="hljs-string">"bar"</span>,
        <span class="hljs-attr">data</span>: {
          <span class="hljs-attr">labels</span>: [ <span class="hljs-string">"Player 1"</span>, <span class="hljs-string">"Player 2"</span>, <span class="hljs-string">"Player 3"</span>, <span class="hljs-string">"Player 4"</span>, <span class="hljs-string">"Player 5"</span>, <span class="hljs-string">"Player 6"</span> ],
          <span class="hljs-attr">datasets</span>: [
            {
              <span class="hljs-attr">label</span>: <span class="hljs-string">"# of points"</span>,
              <span class="hljs-attr">data</span>: [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>],
              <span class="hljs-attr">backgroundColor</span>: [
                <span class="hljs-string">"rgba(255, 99, 132, 0.2)"</span>,
                <span class="hljs-string">"rgba(54, 162, 235, 0.2)"</span>,
                <span class="hljs-string">"rgba(255, 206, 86, 0.2)"</span>,
                <span class="hljs-string">"rgba(75, 192, 192, 0.2)"</span>,
                <span class="hljs-string">"rgba(153, 102, 255, 0.2)"</span>,
                <span class="hljs-string">"rgba(255, 159, 64, 0.2)"</span>,
              ],
              <span class="hljs-attr">borderColor</span>: [
                <span class="hljs-string">"rgba(255, 99, 132, 1)"</span>,
                <span class="hljs-string">"rgba(54, 162, 235, 1)"</span>,
                <span class="hljs-string">"rgba(255, 206, 86, 1)"</span>,
                <span class="hljs-string">"rgba(75, 192, 192, 1)"</span>,
                <span class="hljs-string">"rgba(153, 102, 255, 1)"</span>,
                <span class="hljs-string">"rgba(255, 159, 64, 1)"</span>,
              ],
              <span class="hljs-attr">borderWidth</span>: <span class="hljs-number">1</span>,
            },
          ],
        },
        <span class="hljs-attr">options</span>: {
          <span class="hljs-attr">scales</span>: {
            <span class="hljs-attr">y</span>: {
              <span class="hljs-attr">beginAtZero</span>: <span class="hljs-literal">true</span>,
            },
          },
        },
      });

      webSocket = <span class="hljs-keyword">new</span> WebSocket(<span class="hljs-string">"ws://127.0.0.1:8080"</span>);
      webSocket.onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">message</span>) </span>{
        <span class="hljs-keyword">const</span> data = message.data;
        <span class="hljs-keyword">const</span> dataObj = <span class="hljs-built_in">JSON</span>.parse(data);
        <span class="hljs-keyword">const</span> dataArray = <span class="hljs-built_in">Object</span>.values(dataObj);
        <span class="hljs-built_in">console</span>.log(dataArray);
        index = dataArray[<span class="hljs-number">0</span>] - <span class="hljs-number">1</span>;
        myChart.data.datasets[<span class="hljs-number">0</span>].data[index] = dataArray[<span class="hljs-number">1</span>];
        myChart.update();
      };
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Rundown of the code:</p>
<ul>
<li>We first define the new chart using the <code>Chart.js</code> library: <code>new Chart()</code> and pass the different configuration options.</li>
<li>Then we create a new <code>WebSocket</code> instance and pass it the URL of the Web Socket server with <code>webSocket = new WebSocket("ws://backend:8080");</code></li>
<li>Finally, we define an <code>onmessage</code> event handler on the <code>WebSocket</code> instance, which is called when a message is received and updates the chart.</li>
</ul>
<p>You can find the code in the <a href="https://github.com/bobbyiliev/materialize-tutorials/tree/main/mz-deno-live-dashboard/frontend"><code>frontend</code> directory</a>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>You can leave the Deno application running so that it would be subscribed to the Materialize instance and update the chart in real-time.</p>
<p>As a next step you can check out the Materialize + dbt + Redpanda demo which is based on the same user reviews mock data:</p>
<blockquote>
<p><a href="https://devdojo.com/bobbyiliev/how-to-use-dbt-with-materialize-and-redpanda">Materialize + dbt + Redpanda demo</a></p>
</blockquote>
<h2 id="helpful-resources">Helpful resources:</h2>
<ul>
<li><a href="https://materialize.com/docs/sql/tail/"><code>TAIL</code></a></li>
<li><a href="https://materialize.com/docs/sql/create-source/"><code>CREATE SOURCE</code></a></li>
<li><a href="https://materialize.com/docs/sql/create-views"><code>CREATE VIEWS</code></a></li>
<li><a href="https://materialize.com/docs/sql/select"><code>SELECT</code></a></li>
</ul>
<h2 id="community">Community</h2>
<p>If you have any questions or comments, please join the <a href="https://materialize.com/s/chat">Materialize Slack Community</a>!</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
]]></description>
                                                            <category>javascript</category>
                                            <category>database</category>
                                            <category>websockets</category>
                                            <category>sql</category>
                                            <category>postgres</category>
                                            <category>deno</category>
                                            <category>webdev</category>
                                            <category>materialize</category>
                                            <category>charts.js</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/8986</guid>
                <pubDate>Thu, 09 Jun 2022 07:05:18 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to use Materialize with DigitalOcean Serverless Functions]]></title>
                <link>https://devdojo.com/bobbyiliev/how-to-use-materialize-with-digitalocean-serverless-functions</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<blockquote>
<p>❗️ This demo includes examples for an unsupported version of <a href="https://materialize.com/docs/lts/">Materialize (0.26.x)</a> ❗️</p>
</blockquote>
<p><a href="https://materialize.com?utm_source=bobbyiliev">Materialize</a> is a streaming database for real-time analytics. It was launched in 2019 to address the growing need for the ability to build real-time applications easily and efficiently on streaming data so that businesses can obtain actionable intelligence from streaming data.</p>
<p>This is a simple example of how to use the DigitalOcean serverless functions to query Materialize.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>DigitalOcean Account</li>
<li><code>doctl</code> CLI installed</li>
<li><a href="https://materialize.com/docs/install?utm_source=bobbyiliev">A running Materialize instance</a></li>
</ul>
<p>You can also find the code from this tutorial here:</p>
<p><a href="https://github.com/bobbyiliev/materialize-tutorials/tree/main/mz-digitalocean-serverless">Serverless Functions Demo code</a></p>
<h2 id="configure-materialize">Configure Materialize</h2>
<p>Once you have your Materialize instance running, let's quickly add some data in there.</p>
<p>You can use <code>psql</code> to access Materialize:</p>
<pre><code class="hljs">psql -U materialize -h localhost -p 6875
</code></pre>
<p>For the sake of simplicity, let's start by creating a simple table:</p>
<pre><code class="hljsphp">CREATE TABLE my_view (
  id INT(<span class="hljs-number">11</span>),
  name VARCHAR(<span class="hljs-number">255</span>) NOT <span class="hljs-keyword">NULL</span>,
);
</code></pre>
<p>After that insert some data:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<pre><code class="hljsgo">INSERT INTO my_view (id, name) VALUES (<span class="hljs-number">1</span>, <span class="hljs-string">'Bobby'</span>), (<span class="hljs-number">2</span>, <span class="hljs-string">'John'</span>), (<span class="hljs-number">3</span>, <span class="hljs-string">'Jane'</span>), (<span class="hljs-number">4</span>, <span class="hljs-string">'Jack'</span>);
</code></pre>
<p>In a real-world scenario, you would probably want to add a source like Kafka and create a materialized view. For this example, we will use a simple table, but you can refer to the <a href="https://materialize.com/docs/">Materialize documentation</a> for more information.</p>
<h2 id="setup-doctl">Setup <code>doctl</code></h2>
<p>Once you have <code>doctl</code> installed as per the <a href="https://docs.digitalocean.com/reference/doctl/how-to/install/">DigitalOcean documentation</a>, you can go ahead and follow the steps here to create a new serverless function:</p>
<p>Start by installing the sandbox support:</p>
<pre><code class="hljs">doctl serverless install
</code></pre>
<p>Connect to the cloud portion of your sandbox:</p>
<pre><code class="hljsperl">doctl sandbox <span class="hljs-keyword">connect</span>
</code></pre>
<p>Initialize a local file system directory for the serverless function:</p>
<pre><code class="hljs">doctl serverless init --language php materialize-php
</code></pre>
<p>Then rename the sample function to <code>materialize-php</code>:</p>
<pre><code class="hljs">mv materialize-php/packages/sample/ materialize-php/packages/materialize-php/
</code></pre>
<p>Note that you can do all this via the DigitalOcean Control Panel rather than the CLI.</p>
<h3 id="create-a-env-file">Create a <code>.env</code> file</h3>
<p>As you will need to pass your Materialize login details securely, you should not define them in the <code>project.yaml</code> file. Instead, you should create a <code>.env</code> file at the root of your project.</p>
<p>Start by creating a <code>.env</code> file at the root of your project.</p>
<pre><code class="hljs">touch materialize-php/.env
</code></pre>
<p>Then add the following contents to the <code>.env</code> file:</p>
<pre><code class="hljsphp"><span class="hljs-comment"># Your Materialize Host</span>
MATERIALIZE_HOST=<span class="hljs-string">""</span>
<span class="hljs-comment"># Your Materialize App Specific Password</span>
MATERIALIZE_PASSWORD=<span class="hljs-string">""</span>
<span class="hljs-comment"># Your Materialize App Username</span>
MATERIALIZE_USER=<span class="hljs-string">""</span>
<span class="hljs-comment"># Materialize Port</span>
MATERIALIZE_PORT=<span class="hljs-string">"6875"</span>
<span class="hljs-comment"># Materialize Database Name</span>
MATERIALIZE_DB=<span class="hljs-string">"materialize"</span>
</code></pre>
<p>Make sure to replace the values with your own.</p>
<h3 id="update-the-project-yml">Update the <code>project.yml</code></h3>
<p>The <code>project.yml</code> contains the information for your serverless function. In there you can specify things like the name of the function, your environment variables, and more.</p>
<p>With your favorite text editor, open the <code>project.yml</code> file and add the following contents:</p>
<pre><code class="hljspowershell">targetNamespace: <span class="hljs-string">''</span>
parameters: {}
environment:
  MATERIALIZE_HOST: <span class="hljs-string">"<span class="hljs-variable">$</span>{MATERIALIZE_HOST}"</span>
  MATERIALIZE_PORT: <span class="hljs-string">"<span class="hljs-variable">$</span>{MATERIALIZE_PORT}"</span>
  MATERIALIZE_DB: <span class="hljs-string">"<span class="hljs-variable">$</span>{MATERIALIZE_DB}"</span>
  MATERIALIZE_USER: <span class="hljs-string">"<span class="hljs-variable">$</span>{MATERIALIZE_USER}"</span>
  MATERIALIZE_PASSWORD: <span class="hljs-string">"<span class="hljs-variable">$</span>{MATERIALIZE_PASSWORD}"</span>
packages:
  - name: materialize<span class="hljs-literal">-php</span>
    environment: {}
    parameters: {}
    annotations: {}
    actions:
      - name: hello
        binary: false
        main: <span class="hljs-string">''</span>
        runtime: <span class="hljs-string">'php:default'</span>
        web: true
        parameters: {}
        environment: {}
        annotations: {}
        limits: {}
</code></pre>
<p>The only thing that we've changed is the environment, where we've specified the database credentials. And also the name of the function: <code>materialize-php</code>.</p>
<h3 id="create-the-serverless-function">Create the serverless function</h3>
<p>This is more or less all the configuration that we have to do. Next, we can create the function.</p>
<p>Let's use the default <code>hello.php</code> file that was automatically created for us when we ran the <code>doctl serverless init</code> command:</p>
<pre><code class="hljs">materialize-php/packages/materialize-php/hello/hello.php
</code></pre>
<p>Edit the file and add the following contents:</p>
<pre><code class="hljsxml"><span class="php"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-comment">// Function that connects to Materialize Cloud and returns the response</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">connect</span><span class="hljs-params">(string $host, int $port, string $db, string $user, string $password)</span>: <span class="hljs-title">PDO</span>
</span>{
    <span class="hljs-keyword">try</span> {
        $dsn = <span class="hljs-string">"pgsql:host=$host;port=$port;dbname=$db;"</span>;
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> PDO(
            $dsn,
            $user,
            $password,
            [PDO::ATTR_ERRMODE =&gt; PDO::ERRMODE_EXCEPTION]
        );
    } <span class="hljs-keyword">catch</span> (PDOException $e) {
        <span class="hljs-keyword">die</span>($e-&gt;getMessage());
    }
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> : <span class="hljs-title">array</span>
</span>{
    $host = getenv(<span class="hljs-string">'MATERIALIZE_HOST'</span>);
    $port = getenv(<span class="hljs-string">'MATERIALIZE_PORT'</span>);
    $db = getenv(<span class="hljs-string">'MATERIALIZE_DB'</span>);
    $user = getenv(<span class="hljs-string">'MATERIALIZE_USER'</span>);
    $password = getenv(<span class="hljs-string">'MATERIALIZE_PASSWORD'</span>);

    $connection = connect($host, $port, $db, $user, $password);

    $sql = <span class="hljs-string">'SELECT * FROM my_view'</span>;

    $statement = $connection-&gt;query($sql);
    $results = [];
    <span class="hljs-keyword">while</span> (($row = $statement-&gt;fetch(PDO::FETCH_ASSOC)) !== <span class="hljs-keyword">false</span>) {
        $results[] = $row;
    }

    <span class="hljs-keyword">return</span> [<span class="hljs-string">"body"</span> =&gt; $results];
}
</span></code></pre>
<p>A rundown of the function:</p>
<ul>
<li><code>function connect()</code>: We start by creating a function that we can reuse to connect to Materialize. As Materialize is a PostgreSQL wire-compatible, we just use the default PHP <code>PDO</code> class to connect.</li>
<li><code>function main()</code>: This is the function that will be called when we want to run the function. As you can see we don't call the function directly, but instead we call it from the <code>doctl serverless invoke</code> command or access it via the URL provided by the DigitalOcean.</li>
<li>Inside the <code>main()</code> function we run a simple query to get the data from Materialize and return it.</li>
</ul>
<h2 id="deploy-the-function">Deploy the function</h2>
<p>Finally, to deploy the serverless function, we can run:</p>
<pre><code class="hljs">doctl serverless deploy materialize-php --env materialize-php/.env
</code></pre>
<p>This will deploy the function to DigitalOcean and create a URL that we can access to invoke the function.</p>
<p>To get the URL, you can run:</p>
<pre><code class="hljsjavascript">doctl sbx fn <span class="hljs-keyword">get</span> materialize-php/hello --url
</code></pre>
<p>Alternatively, you can also invoke the function directly with the following command:</p>
<pre><code class="hljs">doctl serverless functions invoke materialize-php/hello
</code></pre>
<p>You will get the following response:</p>
<pre><code class="hljsgo">{
    <span class="hljs-string">"body"</span>: [
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-number">1</span>,
            <span class="hljs-string">"name"</span>: <span class="hljs-string">"Bobby"</span>
        },
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-number">2</span>,
            <span class="hljs-string">"name"</span>: <span class="hljs-string">"Jane"</span>
        },
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-number">3</span>,
            <span class="hljs-string">"name"</span>: <span class="hljs-string">"John"</span>
        },
        {
            <span class="hljs-string">"id"</span>: <span class="hljs-number">4</span>,
            <span class="hljs-string">"name"</span>: <span class="hljs-string">"Jack"</span>
        }
    ]
}
</code></pre>
<p>As a further step, we can extend the function to accept different parameters and return different data like sorting and filtering.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The new serverless functions allow you to quickly deploy your code without having to worry about the details of the infrastructure. Being able to query Materialize from your serverless function is a great way to get real-time data for your functions.</p>
<p>I am planning to add more examples of serverless functions with different languages. You will be able to find them at:</p>
<blockquote>
<p>https://github.com/bobbyiliev/materialize-tutorials/tree/main/mz-digitalocean-serverless</p>
</blockquote>
<h2 id="helpful-resources">Helpful resources:</h2>
<ul>
<li><a href="https://materialize.com/docs/sql/create-source/postgres?utm_source=bobbyiliev"><code>CREATE SOURCE: PostgreSQL</code></a></li>
<li><a href="https://materialize.com/docs/sql/create-source?utm_source=bobbyiliev"><code>CREATE SOURCE</code></a></li>
<li><a href="https://materialize.com/docs/sql/create-views?utm_source=bobbyiliev"><code>CREATE VIEWS</code></a></li>
<li><a href="https://materialize.com/docs/sql/select?utm_source=bobbyiliev"><code>SELECT</code></a></li>
</ul>
<h2 id="community">Community</h2>
<p>If you have any questions or comments, please join the <a href="https://materialize.com/s/chat">Materialize Slack Community</a>!</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
]]></description>
                                                            <category>javascript</category>
                                            <category>php</category>
                                            <category>sql</category>
                                            <category>postgres</category>
                                            <category> DigitalOcean</category>
                                            <category>linux</category>
                                            <category>Serverless</category>
                                            <category> data</category>
                                            <category>devops</category>
                                            <category>materialize</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/8897</guid>
                <pubDate>Fri, 27 May 2022 10:09:32 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Python Script to Copy The Content of a Materialized View to S3]]></title>
                <link>https://devdojo.com/bobbyiliev/python-script-to-copy-the-content-of-a-materialized-view-to-s3</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<blockquote>
<p>❗️ This demo includes examples for an unsupported version of <a href="https://materialize.com/docs/lts/">Materialize (0.26.x)</a> ❗️</p>
</blockquote>
<p><a href="https://materialize.com?utm_source=bobbyiliev">Materialize</a> is a streaming database for real-time analytics. It was launched in 2019 to address the growing need for the ability to build real-time applications easily and efficiently on streaming data so that businesses can obtain actionable intelligence from streaming data.</p>
<p>This is a simple example of how to copy the content of a Materialize view into a local file and upload it to S3.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>Before you can run this script, you need to have the following prerequisites:</p>
<ul>
<li><a href="https://materialize.com/docs/install?utm_source=bobbyiliev">A running Materialize instance</a></li>
<li>Python 3.6 or later</li>
<li>AWS S3 Bucket and credentials</li>
</ul>
<h2 id="install-the-python-dependencies">Install the Python Dependencies</h2>
<p>As Materialize is Postgres wire-compatible, we can use the <code>psycopg2</code> Python library to connect and execute queries just like we would with a Postgres database.</p>
<p>Start by installing the <code>psycopg2</code> Python library:</p>
<pre><code class="hljs">pip3 install psycopg2
</code></pre>
<p>If you are also planning to upload the data to S3, you will need to install the <code>boto3</code> Python library:</p>
<pre><code class="hljs">pip3 install boto3
</code></pre>
<p>With that done, you can now run the script!</p>
<p>If you are going to upload the data to S3, you will need to create an AWS S3 Bucket and credentials in <code>~/.aws/credentials</code>:</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<pre><code class="hljsxml">[default]
aws_access_key_id = <span class="hljs-tag">&lt;<span class="hljs-name">your</span> <span class="hljs-attr">access</span> <span class="hljs-attr">key</span>&gt;</span>
aws_secret_access_key = <span class="hljs-tag">&lt;<span class="hljs-name">your</span> <span class="hljs-attr">secret</span> <span class="hljs-attr">key</span>&gt;</span>
</code></pre>
<h2 id="creating-the-script">Creating the Script</h2>
<p>Create a new Python script file and paste the following code into it:</p>
<pre><code class="hljspython"><span class="hljs-comment">#!/usr/bin/env python3</span>

<span class="hljs-keyword">import</span> psycopg2
<span class="hljs-keyword">import</span> sys
<span class="hljs-keyword">import</span> datetime
<span class="hljs-keyword">import</span> boto3
<span class="hljs-keyword">from</span> botocore.client <span class="hljs-keyword">import</span> Config

<span class="hljs-comment"># Connect to the Materialize database</span>
<span class="hljs-comment"># Replace the values with your own</span>
dsn = <span class="hljs-string">"postgresql://materialize@localhost:6875/materialize?sslmode=disable"</span>
conn = psycopg2.connect(dsn)

<span class="hljs-comment"># If arg supply don't ask for input</span>
<span class="hljs-keyword">if</span> len(sys.argv) &gt; <span class="hljs-number">1</span>:
    mz_view = sys.argv[<span class="hljs-number">1</span>]
<span class="hljs-keyword">else</span>:
    mz_view = input(<span class="hljs-string">"Enter your view name: "</span>)
    <span class="hljs-comment"># While empty ask for input</span>
    <span class="hljs-keyword">while</span> mz_view == <span class="hljs-string">""</span>:
        mz_view = input(<span class="hljs-string">"Enter your view name: "</span>)

<span class="hljs-comment"># Check if view exists</span>
print(<span class="hljs-string">"Checking if view exists..."</span>)
<span class="hljs-keyword">with</span> conn.cursor() <span class="hljs-keyword">as</span> cur:
    <span class="hljs-keyword">try</span>:
        cur.execute(<span class="hljs-string">"SELECT * FROM "</span> + mz_view + <span class="hljs-string">" LIMIT 1"</span>)
    <span class="hljs-keyword">except</span> psycopg2.Error <span class="hljs-keyword">as</span> e:
        print(<span class="hljs-string">"View "</span> + mz_view + <span class="hljs-string">" doesn't exist"</span>)
        sys.exit(<span class="hljs-number">1</span>)

<span class="hljs-comment"># File name with timestamp without spaces</span>
mz_file = mz_view + <span class="hljs-string">"_"</span> + str(datetime.datetime.now()).replace(<span class="hljs-string">" "</span>, <span class="hljs-string">"_"</span>) + <span class="hljs-string">".txt"</span>

print(<span class="hljs-string">"Dumping view "</span> + mz_view + <span class="hljs-string">" to file "</span> + mz_file)
<span class="hljs-keyword">with</span> conn.cursor() <span class="hljs-keyword">as</span> cur:
    cur.execute(<span class="hljs-string">"SELECT * FROM "</span> + mz_view)
    <span class="hljs-keyword">for</span> row <span class="hljs-keyword">in</span> cur:
        <span class="hljs-keyword">with</span> open(mz_file, <span class="hljs-string">'a'</span>) <span class="hljs-keyword">as</span> f:
            f.write(str(row) + <span class="hljs-string">'\n'</span>)

<span class="hljs-comment"># Ask if want to upload to S3</span>
<span class="hljs-keyword">if</span> input(<span class="hljs-string">"Upload to S3? (y/n): "</span>) == <span class="hljs-string">"y"</span>:
    <span class="hljs-keyword">if</span> len(sys.argv) &gt; <span class="hljs-number">2</span>:
        BUCKET = sys.argv[<span class="hljs-number">2</span>]
    <span class="hljs-keyword">else</span>:
        BUCKET = input(<span class="hljs-string">"Enter your S3 bucket name: "</span>)
        <span class="hljs-comment"># While empty ask for input</span>
        <span class="hljs-keyword">while</span> BUCKET == <span class="hljs-string">""</span>:
            BUCKET = input(<span class="hljs-string">"Enter your S3 bucket name: "</span>)

    s3 = boto3.resource(<span class="hljs-string">'s3'</span>)
    <span class="hljs-comment"># MinIo Example:</span>
    <span class="hljs-comment"># s3 = boto3.resource('s3',</span>
    <span class="hljs-comment">#                 endpoint_url='http://localhost:9000',</span>
    <span class="hljs-comment">#                 aws_access_key_id='YOUR-ACCESSKEYID',</span>
    <span class="hljs-comment">#                 aws_secret_access_key='YOUR-SECRETACCESSKEY',</span>
    <span class="hljs-comment">#                 config=Config(signature_version='s3v4'),</span>
    <span class="hljs-comment">#                 region_name='us-east-1')</span>

    print(<span class="hljs-string">"Uploading file "</span> + mz_file + <span class="hljs-string">" to S3 bucket "</span> + BUCKET)
    <span class="hljs-keyword">try</span>:
        s3.Bucket(BUCKET).upload_file(mz_file, <span class="hljs-string">"mz_dump/"</span> + mz_file)
    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
        print(<span class="hljs-string">"Error uploading file to S3: "</span> + str(e))
        sys.exit(<span class="hljs-number">1</span>)
</code></pre>
<p>You can also get the script from <a href="https://github.com/bobbyiliev/materialize-tutorials/">GitHub</a></p>
<h2 id="running-the-script">Running the Script</h2>
<p>To run the script, you need to supply the name of the view you want to copy.</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">python3</span> <span class="hljs-selector-tag">mz-copy</span><span class="hljs-selector-class">.py</span>
</code></pre>
<p>You will be asked for the name of the view and the name of the S3 bucket in case you want to upload the data to S3:</p>
<pre><code class="hljsperl">Enter your view name: my_view
Checking <span class="hljs-keyword">if</span> view exists...
Dumping view test to file my_view_2022-<span class="hljs-number">05</span>-<span class="hljs-number">27_14</span>:<span class="hljs-number">52</span>:<span class="hljs-number">02</span>.<span class="hljs-number">737658</span>.txt
Upload to S3? (<span class="hljs-keyword">y</span>/n): <span class="hljs-keyword">y</span>
Enter your S3 bucket name: my_bucker
</code></pre>
<p>You can also specify the name of the view and the S3 bucket as arguments:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">python3</span> <span class="hljs-selector-tag">mz-copy</span><span class="hljs-selector-class">.py</span> <span class="hljs-selector-tag">my_view</span> <span class="hljs-selector-tag">my_bucket</span>
</code></pre>
<p>This will generate a file named <code>my_view_2022-05-27_14:52:02.737658.txt</code> and upload it to the S3 bucket <code>my_bucket</code>.</p>
<h2 id="using-minio">Using MinIO</h2>
<p>To use MinIO instead of S3, you can change the <code>s3 = boto3.resource('s3')</code> line to:</p>
<pre><code class="hljsgo">s3 = boto3.resource(<span class="hljs-string">'s3'</span>,
    endpoint_url=<span class="hljs-string">'http://localhost:9000'</span>,
    aws_access_key_id=<span class="hljs-string">'YOUR-ACCESSKEYID'</span>,
    aws_secret_access_key=<span class="hljs-string">'YOUR-SECRETACCESSKEY'</span>,
    config=Config(signature_version=<span class="hljs-string">'s3v4'</span>),
    region_name=<span class="hljs-string">'us-east-1'</span>)
</code></pre>
<p>For more information on MinIO, please refer to <a href="https://docs.min.io/docs/python-client-api-reference">this article</a>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>This is a simple example of how to copy the content of a Materialize view into a local file and upload it to S3.</p>
<p>There are plans to add is something similar to Postgres's <code>COPY</code> command for exactly this purpose, but in the meantime, you can use this script to dump the data into a local file and upload it to S3.</p>
<p>At the moment you can use <code>COPY</code> send the output to <code>STDOUT</code> example:</p>
<pre><code class="hljs">COPY (SELECT * FROM some_view) TO STDOUT;
</code></pre>
<h2 id="helpful-resources">Helpful resources:</h2>
<ul>
<li><a href="https://materialize.com/docs/sql/create-source/postgres?utm_source=bobbyiliev"><code>CREATE SOURCE: PostgreSQL</code></a></li>
<li><a href="https://materialize.com/docs/sql/create-source?utm_source=bobbyiliev"><code>CREATE SOURCE</code></a></li>
<li><a href="https://materialize.com/docs/sql/create-views?utm_source=bobbyiliev"><code>CREATE VIEWS</code></a></li>
<li><a href="https://materialize.com/docs/sql/select?utm_source=bobbyiliev"><code>SELECT</code></a></li>
</ul>
<h2 id="community">Community</h2>
<p>If you have any questions or comments, please join the <a href="https://materialize.com/s/chat">Materialize Slack Community</a>!</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
]]></description>
                                                            <category>database</category>
                                            <category>sql</category>
                                            <category>Python</category>
                                            <category>postgres</category>
                                            <category>linux</category>
                                            <category>script</category>
                                            <category> data</category>
                                            <category>devops</category>
                                            <category>webdev</category>
                                            <category>materialize</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/8894</guid>
                <pubDate>Fri, 27 May 2022 05:51:32 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Using Deno and Materialize to send notifications in real-time]]></title>
                <link>https://devdojo.com/bobbyiliev/using-deno-and-materialize-to-send-notifications-in-real-time</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<blockquote>
<p>❗️ This demo includes examples for an unsupported version of <a href="https://materialize.com/docs/lts/">Materialize (0.26.x)</a> ❗️</p>
</blockquote>
<p>This is a self-contained example of a real-time notification app with Deno, <a href="https://materialize.com">Materialize</a> and Discord.</p>
<p><a href="https://deno.land/">Deno</a> is a simple and secure runtime for JavaScript and TypeScript that uses V8. Deno, just like Materialize, is also written in Rust.</p>
<p>In this demo, we will build a simple notifications app. The notifications are going to be triggered when a user creates a bad review for a product. The reviews are generated by a mock service and stored in a Kafka/Redpanda topic. The data is then ingested into a live Materialize view which is kept up to date in real-time.</p>
<p>We then use Deno to connect to Materialize and send notifications to a Discord channel when a user creates a bad review.</p>
<p>That way we can send real-time notifications to a Discord channel and take action immediately to help the user with the issue they are facing with the product.</p>
<h2 id="overview">Overview</h2>
<p>Here is a quick overview of the project:</p>
<ul>
<li>A mock service to continually generate reviews and users.</li>
<li>Redpanda instance to store the reviews and users in topics.</li>
<li>Materialize instance that is connected to the Redpanda and joins the reviews and users topics in a live materialized view which we can query in real-time using just SQL.</li>
<li>A Deno backend service that connects to Materialize and sends notifications to a Discord channel for each bad review submitted by a user with a role of <code>vip</code>.</li>
</ul>
<p>Here is a diagram of the project:</p>
<p><img src="https://imgur.com/l2EejrO.png" alt="Deno and materialize demo project"></p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<h2 id="prerequisites">Prerequisites</h2>
<p>To run this demo, you need to have the following installed.</p>
<ul>
<li><a href="https://docs.docker.com/get-docker/">Install Docker</a>.</li>
<li><a href="https://docs.docker.com/compose/install/">Install Docker Compose</a>.</li>
<li><a href="https://deno.land/manual/getting_started/installation">Install Deno</a>.</li>
</ul>
<p>Optionally, if you want to use the Discord webhook integration, you need to create a Discord channel and get a <a href="https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks">webhook integration URL</a>. You can skip that step if you don't want to use it, and you can just see the notifications in the console.</p>
<h2 id="running-the-demo">Running the demo</h2>
<p>To get started, clone the repository:</p>
<pre><code class="hljsphp">git <span class="hljs-keyword">clone</span> git <span class="hljs-keyword">clone</span> https:<span class="hljs-comment">//github.com/bobbyiliev/materialize-tutorials.git</span>
</code></pre>
<p>Then you can access the directory:</p>
<pre><code class="hljs">cd materialize-tutorials
git checkout lts
cd mz-deno-discord-live-notifications
</code></pre>
<p>With that you can then build the images:</p>
<pre><code class="hljs">docker-compose build
</code></pre>
<p>And finally, you can run all the containers:</p>
<pre><code class="hljs">docker-compose up -d
</code></pre>
<p>It might take a couple of minutes to start the containers and generate the demo data.</p>
<p>After that, we are ready to complete the Materialize setup.</p>
<h2 id="materialize-setup">Materialize setup</h2>
<p>Now that we have the containers running, we need to set up the Materialize instance.</p>
<p>First, launch the Materialize CLI:</p>
<pre><code class="hljs">docker-compose run mzcli
</code></pre>
<blockquote>
<p>This is just a shortcut to a docker container with postgres-client pre-installed, if you already have <code>psql</code> you could run <code>psql -U materialize -h localhost -p 6875 materialize</code>.</p>
</blockquote>
<p>Once you are in the Materialize CLI, let's start by creating the Kafka sources!</p>
<h3 id="create-the-kafka-sources">Create the Kafka sources</h3>
<p>By using the <code>CREATE SOURCE</code> statement, we can connect Materialize to an external data source and lets us interact with its data as if the data were in a SQL table.</p>
<p>To create the sources, execute the following statements:</p>
<pre><code class="hljsgo">CREATE SOURCE users
FROM KAFKA BROKER <span class="hljs-string">'redpanda:9092'</span> TOPIC <span class="hljs-string">'users_topic'</span>
FORMAT BYTES;

CREATE SOURCE roles
FROM KAFKA BROKER <span class="hljs-string">'redpanda:9092'</span> TOPIC <span class="hljs-string">'roles_topic'</span>
FORMAT BYTES;

CREATE SOURCE reviews
FROM KAFKA BROKER <span class="hljs-string">'redpanda:9092'</span> TOPIC <span class="hljs-string">'reviews_topic'</span>
FORMAT BYTES;
</code></pre>
<p>Creating the sources in Materialize does not actually start the data ingestion. You can think of a non-materialized source as just the metadata needed for Materialize to connect to your source but not process any data.</p>
<h3 id="create-non-materialized-views">Create non-materialized views</h3>
<p>Once we have our sources ready, let's <a href="https://materialize.com/docs/sql/create-view/">create non-materialized views</a>, that essentially only provide us with an alias for the <code>SELECT</code> statements they include:</p>
<ul>
<li>Create a users view:</li>
</ul>
<pre><code class="hljsphp">CREATE VIEW users_view <span class="hljs-keyword">AS</span>
    SELECT
        *
    FROM (
        SELECT
            (data-&gt;&gt;<span class="hljs-string">'id'</span>)::int <span class="hljs-keyword">AS</span> id,
            (data-&gt;&gt;<span class="hljs-string">'username'</span>)::text <span class="hljs-keyword">AS</span> username,
            (data-&gt;&gt;<span class="hljs-string">'email'</span>)::text <span class="hljs-keyword">AS</span> email,
            (data-&gt;&gt;<span class="hljs-string">'role_id'</span>)::int <span class="hljs-keyword">AS</span> role_id
        FROM (
            SELECT CAST(data <span class="hljs-keyword">AS</span> jsonb) <span class="hljs-keyword">AS</span> data
            FROM (
                SELECT convert_from(data, <span class="hljs-string">'utf8'</span>) <span class="hljs-keyword">AS</span> data
                FROM users
            )
        )
    );
</code></pre>
<ul>
<li>Create the roles view:</li>
</ul>
<pre><code class="hljsphp">CREATE VIEW roles_view <span class="hljs-keyword">AS</span>
    SELECT
        *
    FROM (
        SELECT
            (data-&gt;&gt;<span class="hljs-string">'id'</span>)::int <span class="hljs-keyword">AS</span> id,
            (data-&gt;&gt;<span class="hljs-string">'name'</span>)::text <span class="hljs-keyword">AS</span> name
        FROM (
            SELECT CAST(data <span class="hljs-keyword">AS</span> jsonb) <span class="hljs-keyword">AS</span> data
            FROM (
                SELECT convert_from(data, <span class="hljs-string">'utf8'</span>) <span class="hljs-keyword">AS</span> data
                FROM roles
            )
        )
    );
</code></pre>
<ul>
<li>Create the reviews view:</li>
</ul>
<pre><code class="hljsphp">CREATE VIEW reviews_view <span class="hljs-keyword">AS</span>
    SELECT
        *
    FROM (
        SELECT
            (data-&gt;&gt;<span class="hljs-string">'user_id'</span>)::int <span class="hljs-keyword">AS</span> user_id,
            (data-&gt;&gt;<span class="hljs-string">'rating'</span>)::int <span class="hljs-keyword">AS</span> rating,
            (data-&gt;&gt;<span class="hljs-string">'review_text'</span>)::text <span class="hljs-keyword">AS</span> review_text,
            (data-&gt;&gt;<span class="hljs-string">'created_at'</span>)::double <span class="hljs-keyword">AS</span> created_at
        FROM (
            SELECT CAST(data <span class="hljs-keyword">AS</span> jsonb) <span class="hljs-keyword">AS</span> data
            FROM (
                SELECT convert_from(data, <span class="hljs-string">'utf8'</span>) <span class="hljs-keyword">AS</span> data
                FROM reviews
            )
        )
    );
</code></pre>
<p>Essentially, as the data in our Redpanda topics is JSON, we can use the <code>CAST</code> function to convert the data to a JSONB object and then use the <code>-&gt;&gt;</code> operator to access the different fields. That way we can access the data in a more convenient way. For more information, check out the <a href="https://materialize.com/docs/sql/create-source/kafka/#creating-a-source">Materialize documentation</a>.</p>
<h3 id="create-the-materialized-views">Create the materialized views</h3>
<p>Now that we have our sources ready, let's create the materialized views!</p>
<p>The <code>CREATE MATERIALIZED VIEW</code> statement creates a materialized view that computes and maintains the results of a <code>SELECT</code> query in memory. The results of the query are incrementally updated in real-time as new data is added to the source.</p>
<p>To create the materialized views we need to execute the following statements:</p>
<ul>
<li>Join <code>users</code> and <code>roles</code> and only get the users with role <code>vip</code>:</li>
</ul>
<pre><code class="hljsphp">CREATE MATERIALIZED VIEW vip_users <span class="hljs-keyword">AS</span>
    SELECT
        u.id,
        u.username,
        u.email,
        u.role_id,
        r.name
    FROM users_view u
    JOIN roles_view r ON u.role_id = r.id
    WHERE r.name = <span class="hljs-string">'vip'</span>;
</code></pre>
<ul>
<li>And for the reviews, we would want to only materialize the reviews that were generated in the last 10 minutes and only get the reviews for the users with role <code>vip</code>:</li>
</ul>
<pre><code class="hljsphp">CREATE MATERIALIZED VIEW bad_vip_reviews <span class="hljs-keyword">AS</span>
    SELECT
        r.user_id,
        r.rating,
        r.review_text,
        r.created_at,
        u.username,
        u.email
    FROM reviews_view r
    JOIN vip_users u ON r.user_id = u.id
    WHERE r.rating &lt; <span class="hljs-number">3</span>
    <span class="hljs-keyword">AND</span>
        mz_logical_timestamp() &lt; (created_at*<span class="hljs-number">1000</span> + <span class="hljs-number">100000</span>)::numeric;
</code></pre>
<p>For more information on how the <code>mz_logical_timestamp()</code> function works, see the <a href="https://materialize.com/docs/sql/functions/now_and_mz_logical_timestamp">Materialize documentation</a>.</p>
<p>You can now query the data in the materialized views using standard SQL!</p>
<pre><code class="hljs">SELECT * FROM bad_vip_reviews LIMIT 10;
</code></pre>
<p>You can run the query multiple times to see the results change.</p>
<h3 id="using-tail">Using <code>TAIL</code></h3>
<p>Next, to see the results in real-time we can use <code>TAIL</code>:</p>
<pre><code class="hljs">COPY ( TAIL bad_vip_reviews ) TO STDOUT;
</code></pre>
<p>You will see a flow of the reviews that were generated in real-time. As we've used the <code>mz_logical_timestamp()</code> function, you will see that the records which are older than 10 minutes are being filtered out, which is indicated with a <code>-1</code>.</p>
<p>We can also start a <code>TAIL</code> without a snapshot, which means that you will only see the latest records after the query is run:</p>
<pre><code class="hljsgo">COPY ( TAIL bad_vip_reviews WITH (SNAPSHOT = <span class="hljs-literal">false</span>) ) TO STDOUT;
</code></pre>
<p>This is what we will see in our Deno application to get the latest bad reviews and send them to Discord as notifications when they are generated.</p>
<p>For more information on how the <code>TAIL</code> function works, see the <a href="https://materialize.com/docs/sql/tail/">Materialize documentation</a>.</p>
<h2 id="using-deno">Using Deno</h2>
<p>Now that we have Materialize ready, we can use Deno to get the latest reviews and send them to Discord as notifications when they are generated.</p>
<p>Start by checking if Deno is installed:</p>
<pre><code class="hljs">deno --version
</code></pre>
<p>If you don't have Deno installed, follow the steps here:</p>
<blockquote>
<p><a href="https://deno.land/manual/getting_started/installation">Install Deno</a></p>
</blockquote>
<p>Next, create a new file called <code>index.ts</code>:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">touch</span> <span class="hljs-selector-tag">index</span><span class="hljs-selector-class">.ts</span>
</code></pre>
<p>And add the following code:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">import</span> { Client } <span class="hljs-keyword">from</span> <span class="hljs-string">"https://deno.land/x/postgres/mod.ts"</span>;

<span class="hljs-comment">// Specify your Discord webhook URL</span>
<span class="hljs-keyword">const</span> discord_webhook_url = <span class="hljs-string">""</span>;

<span class="hljs-comment">// Specify your Materialize connection details</span>
<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> Client({
    <span class="hljs-attr">user</span>: <span class="hljs-string">"materialize"</span>,
    <span class="hljs-attr">database</span>: <span class="hljs-string">"materialize"</span>,
    <span class="hljs-attr">hostname</span>: <span class="hljs-string">"127.0.0.1"</span>,
    <span class="hljs-attr">port</span>: <span class="hljs-number">6875</span>,
});

<span class="hljs-keyword">const</span> tail = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-comment">// Connect to Materialize</span>
    <span class="hljs-keyword">await</span> client.connect();

    <span class="hljs-comment">// Start a transaction</span>
    <span class="hljs-keyword">await</span> client.queryObject(<span class="hljs-string">'BEGIN'</span>);
    <span class="hljs-comment">// Declare a cursor without a snapshot</span>
    <span class="hljs-keyword">await</span> client.queryObject(<span class="hljs-string">'DECLARE c CURSOR FOR TAIL bad_vip_reviews WITH (SNAPSHOT = false)'</span>);

    <span class="hljs-comment">// Start a loop to get the latest records</span>
    <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) {
        <span class="hljs-comment">// Get the next record</span>
        <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> client.queryObject&lt;{ <span class="hljs-attr">mz_timestamp</span>: string; mz_diff: number, <span class="hljs-attr">user_id</span>: number, 
<span class="hljs-attr">rating</span>: number, <span class="hljs-attr">review_text</span>: number, <span class="hljs-attr">created_at</span>: string, <span class="hljs-attr">username</span>: string, <span class="hljs-attr">email</span>: string }&gt;(<span class="hljs-string">'FETCH ALL c'</span>);

        <span class="hljs-comment">// Loop through the records and send them to Discord</span>
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> row <span class="hljs-keyword">of</span> result.rows) {
            <span class="hljs-keyword">if</span> (row.mz_diff &gt; <span class="hljs-number">0</span>) {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`<span class="hljs-subst">${row.username}</span> has left a bad review with rating <span class="hljs-subst">${row.rating}</span>. Email: <span class="hljs-subst">${row.email}</span>`</span>);
                <span class="hljs-comment">// Make an HTTP request to post to the Discord webhook</span>
                <span class="hljs-keyword">if</span> (discord_webhook_url) {
                    <span class="hljs-keyword">await</span> fetch(discord_webhook_url, {
                        <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
                        <span class="hljs-attr">headers</span>: {
                            <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
                        },
                        <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
                            <span class="hljs-attr">content</span>: <span class="hljs-string">`<span class="hljs-subst">${row.username}</span> has left a bad review!\nRating <span class="hljs-subst">${row.rating}</span>.\nEmail: <span class="hljs-subst">${row.email}</span>`</span>,
                        }),
                    });
                }
            }
        }
    }

    <span class="hljs-keyword">await</span> client.end();
}

tail();

</code></pre>
<p>Rundown of the code:</p>
<ul>
<li>As Materialize is Postgres wire compatible, first we import the <code>Client</code> class from the <code>https://deno.land/x/postgres/mod.ts</code> module. This is the class that we will use to connect to the Materialize instance.</li>
<li>Then, you can specify the Discord webhook URL which you want to send the notifications to. If you don't want to send notifications, you can leave this empty and the code will just print the reviews to the console.</li>
<li>Next, we create a new <code>Client</code> instance and pass it the credentials for Materialize.</li>
<li>Then we call the <code>connect()</code> method on the client instance to connect to Materialize.</li>
<li>Next, we call the <code>queryObject()</code> method on the client instance to start a transaction and also call the <code>queryObject()</code> method on the client instance to declare a cursor without a snapshot.</li>
<li>Finally, we start a loop to get the latest records.</li>
<li>In the loop, we call the <code>queryObject()</code> method on the client instance to get the next record.</li>
<li>In the loop, we loop through the records and send them to Discord.</li>
</ul>
<p>Then run the application:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">deno</span> <span class="hljs-selector-tag">run</span> <span class="hljs-selector-tag">--allow-net</span> <span class="hljs-selector-tag">--allow-read</span> <span class="hljs-selector-tag">--allow-env</span> <span class="hljs-selector-tag">--unstable</span> <span class="hljs-selector-tag">index</span><span class="hljs-selector-class">.ts</span>
</code></pre>
<p>Sample output:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">user7872</span> <span class="hljs-selector-tag">has</span> <span class="hljs-selector-tag">left</span> <span class="hljs-selector-tag">a</span> <span class="hljs-selector-tag">bad</span> <span class="hljs-selector-tag">review</span> <span class="hljs-selector-tag">with</span> <span class="hljs-selector-tag">rating</span> 2. <span class="hljs-selector-tag">Email</span>: <span class="hljs-selector-tag">user7872</span><span class="hljs-keyword">@example</span>.com
user7881 has left a bad review with rating <span class="hljs-number">2</span>. <span class="hljs-attribute">Email:</span> user7881@example.com
user7887 has left a bad review with rating <span class="hljs-number">2</span>. <span class="hljs-attribute">Email:</span> user7887@example.com
user7890 has left a bad review with rating <span class="hljs-number">1</span>. <span class="hljs-attribute">Email:</span> user7890@example.com
user7894 has left a bad review with rating <span class="hljs-number">1</span>. <span class="hljs-attribute">Email:</span> user7894@example.com
...
</code></pre>
<p>The same information will also be sent to Discord as a notification:</p>
<p><img src="https://imgur.com/I50scX0.png" alt="Discord notification"></p>
<h2 id="conclusion">Conclusion</h2>
<p>You can leave the Deno application running so that it would be subscribed to the Materialize instance and send the latest reviews to Discord as notifications.</p>
<p>As a next step, you can implement some error handling for the application so that it would gracefully handle errors and send the error to Discord as a notification.</p>
<p>As a next step you can check out the Materialize + dbt + Redpanda demo which is based on the same user reviews mock data:</p>
<blockquote>
<p><a href="https://devdojo.com/bobbyiliev/how-to-use-dbt-with-materialize-and-redpanda">Materialize + dbt + Redpanda demo</a></p>
</blockquote>
<p>As an extra step for this tutorial, you can try using the <a href="https://documentation-apidocumentation.trustpilot.com/">TrustPilot API</a> to get accurate ratings rather than the mock data.</p>
<h2 id="helpful-resources">Helpful resources:</h2>
<ul>
<li><a href="https://materialize.com/docs/sql/tail/"><code>TAIL</code></a></li>
<li><a href="https://materialize.com/docs/sql/create-source/"><code>CREATE SOURCE</code></a></li>
<li><a href="https://materialize.com/docs/sql/create-views"><code>CREATE VIEWS</code></a></li>
<li><a href="https://materialize.com/docs/sql/select"><code>SELECT</code></a></li>
</ul>
<h2 id="community">Community</h2>
<p>If you have any questions or comments, please join the <a href="https://materialize.com/s/chat">Materialize Slack Community</a>!</p>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
]]></description>
                                                            <category>javascript</category>
                                            <category>node</category>
                                            <category>database</category>
                                            <category>sql</category>
                                            <category>deno</category>
                                            <category>webdev</category>
                                            <category>materialize</category>
                                            <category>streaming data</category>
                                            <category>redpanda</category>
                                            <category>kafka</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/8765</guid>
                <pubDate>Fri, 13 May 2022 08:47:16 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Scaling Laravel with Serverless Redis]]></title>
                <link>https://devdojo.com/bobbyiliev/scaling-laravel-with-serverless-redis</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Laravel is a popular PHP framework for building scalable, high-performance web applications.</p>
<p>In this article, we will learn how to use <a href="https://upstash.com/redis?utm_source=bobby1">serverless Redis</a> to scale Laravel applications by storing the Laravel session and cache data in a serverless Redis instance.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>Before you get started, you'll need to have the following:</p>
<ul>
<li><a href="https://upstash.com?utm_source=bobby1">Upstash account</a>: In case that you don't have one, you can <a href="https://upstash.com/signup?utm_source=bobby1">sign up for free</a>, no credit card required.</li>
<li>If you don't have Laravel installed, you can follow the steps on how to do that here: <a href="https://devdojo.com/bobbyiliev/how-to-install-laravel-on-digitalocean-with-1-click">Install Laravel with 1-click</a></li>
</ul>
<h2 id="architecture-overview">Architecture Overview</h2>
<p>Rather than running Laravel on a single server, let's consider the following scenario:</p>
<ul>
<li>A Laravel application running on two web servers.</li>
<li>A single Load Balancer is responsible for routing requests to the two web servers.</li>
<li>A MySQL database server, used to store the application's data.</li>
<li><a href="https://upstash.com/redis?utm_source=bobby1">Upstash Serverless Redis cluster</a> is responsible for caching data and storing user sessions.</li>
</ul>
<p>Diagram:</p>
<p><img src="https://imgur.com/rFmygO1.png" alt="Scaling Laravel with serverless Redis"></p>
<h2 id="what-is-serverless-redis">What is Serverless Redis?</h2>
<p>Serverless Redis is a fully managed database as a service product where the pricing is based on per command, so you are only charged what you actually use.</p>
<p>That way you don't have to over provision your servers, and you can scale your application as needed.</p>
<h2 id="why-serverless-redis">Why Serverless Redis?</h2>
<p>By default, Laravel would store the user sessions in files on the web server's disk. That way if the load balancer forwards the user request to a different server, the user session would be lost.</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<p>This is why it is important to have a centralized place to store the user sessions and application cache, so that they can be shared between requests and across multiple servers, and not be lost each time the load balancer forwards the request to a different server.</p>
<p>Of course, you can also use your database to store the user sessions and cache data, but for better performance, it is recommended to use Redis for better performance. If you want to learn more about the performance benefits of the different options, check out this great article here: <a href="https://www.georgebuckingham.com/laravel-cache-driver-performance/">Which is the best Laravel cache driver for performance?</a>.</p>
<h2 id="horizontal-scaling-vs-vertical-scaling">Horizontal Scaling vs. Vertical Scaling</h2>
<p>Just a few words about the difference between horizontal and vertical scaling:</p>
<ul>
<li>When you have a single server, you can scale it vertically by adding more resources to it. For example, you can add more CPU cores, RAM, or disk space to <strong>scale-up</strong>.</li>
<li>Horizontal scaling on the other side, is when you add more servers that are responsible for serving requests to <strong>scale-out</strong>.</li>
</ul>
<p>Here is a simple example of horizontal scaling vs. vertical scaling:</p>
<p><img src="https://imgur.com/RAklmV6.png" alt="Horizontal vs vertical scaling"></p>
<p>When horizontally scaling an application, it is important to handle your user sessions and cache data in a scalable way.</p>
<h2 id="creating-a-serverless-redis-cluster">Creating a serverless Redis cluster</h2>
<p>With <a href="https://upstash.com?utm_source=bobby1">Upstash</a>, you can create a serverless Redis cluster in 30 seconds by following these steps:</p>
<ul>
<li>Log in to your <a href="https://console.upstash.com?utm_source=bobby1">Upstash account</a>.</li>
<li>Click on the <strong>Create Database</strong> button.</li>
<li>Enter the name of your Redis cluster and choose a region.</li>
<li>Click on the <strong>Create</strong> button.</li>
</ul>
<p>That's it! You now have a serverless Redis cluster ready to use.</p>
<p>Make sure to note down the endpoint of your Redis cluster along with the password and the port.</p>
<h2 id="configuring-laravel-with-serverless-redis">Configuring Laravel with Serverless Redis</h2>
<p>Now that you have a serverless Redis cluster, you can configure Laravel to use it just as you would any other Redis instance.</p>
<h3 id="install-the-predis-package">Install the Predis package</h3>
<p>In the past, you would use the PHP Redis extension to connect to your Redis cluster. However, now you can use the Predis package instead.</p>
<p>To install the Predis package, run the following command:</p>
<pre><code class="hljsjavascript">composer <span class="hljs-built_in">require</span> predis/predis
</code></pre>
<p>Next, head over to your Laravel project's <code>.env</code> file and update the following lines:</p>
<pre><code class="hljs">REDIS_HOST=your_upstash_redis_endpoint
REDIS_PASSWORD=your_upstash_redis_password
REDIS_PORT=your_upstash_redis_port
</code></pre>
<p>While changing the Redis details, make sure to also change the cache driver and the session driver to <code>redis</code>:</p>
<pre><code class="hljs">CACHE_DRIVER=redis
SESSION_DRIVER=redis
</code></pre>
<p>Finally, clear your config cache by running the following command:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">php</span> <span class="hljs-selector-tag">artisan</span> <span class="hljs-selector-tag">config</span><span class="hljs-selector-pseudo">:clear</span>
</code></pre>
<p>That way your Laravel application will use the serverless Redis cluster to store its cache and session data.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Using Laravel with Serverless Redis is a great way to scale your application. Even if you are running Laravel on a Kubernetes cluster, you can still use a serverless Redis cluster to store your user sessions and cache data in a scalable way.</p>
<p>For more information on Upstash, check their <a href="https://docs.upstash.com/redis?utm_source=bobby1">documentation</a>.</p>
<p>For more information on how to scale your Laravel application check out the following article:</p>
<ul>
<li><a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-a-scalable-laravel-6-application-using-managed-databases-and-object-storage">How to Set Up a Scalable Laravel 6 Application using Managed Databases and Object Storage</a></li>
</ul>
<p>If you are already working as a DevOps engineer, check out this DevOps Scorecard and <a href="https://devops-daily.com/games/devops-scorecard">evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities</a></p>
]]></description>
                                                            <category>php</category>
                                            <category>laravel</category>
                                            <category>linux</category>
                                            <category>Serverless</category>
                                            <category>devops</category>
                                            <category>webdev</category>
                                            <category>redis</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/8684</guid>
                <pubDate>Mon, 09 May 2022 09:05:30 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Using LogicLoop and Materialize  with dbt and Redpanda/Kafka]]></title>
                <link>https://devdojo.com/bobbyiliev/using-logicloop-and-materialize-with-dbt-and-redpandakafka</link>
                <description><![CDATA[<h2 id="introduction">Introduction</h2>
<blockquote>
<p>❗️ This demo includes examples for an unsupported version of <a href="https://materialize.com/docs/lts/">Materialize (0.26.x)</a> ❗️</p>
</blockquote>
<p>LogicLoop allows you to write rules in SQL and then run them against your data and trigger different actions based on the results. LogicLoop also allows you to create and share dashboards and visualizations easily via their web interface.</p>
<p>Materialize is a source-available streaming database that takes data coming from sources like Kafka/Redpanda, Postgres, S3, and more, and allows users to write views that aggregate data on your event stream. The magic is that the views are translated to dataflows which allows Materialize to maintain the views incrementally in real-time. A normal materialized view would do a full scan of the data every time it needs to be updated, Materialize only does the work to maintain the view based on events that come in, so it is much faster and more efficient.</p>
<p>In this tutorial, we will walk through how to use LogicLoop with Materialize.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>Make sure to sign up for a <a href="https://app.logicloop.com/setup">LogicLoop account</a> first.</p>
<p>Also, for this tutorial, we will extend upon the previous article on <a href="https://devdojo.com/bobbyiliev/how-to-use-dbt-with-materialize-and-redpanda">how to use dbt with Materialize and Redpanda</a>.</p>
<p>The architecture of the previous article is as follows:</p>
<p><img src="https://user-images.githubusercontent.com/21223421/148790925-fff39499-d8a3-4b2e-8488-13f61265b0a0.png" alt="How to use dbt with Materialize and Redpanda"></p>
<p>If you want to follow along, make sure to read the previous article first and have the project up and running on your server.</p><div class="relative p-5 pt-8 bg-gray-50 border ignore border-neutral-200 not-prose">
    <div class="absolute w-auto rounded-b-lg border-b uppercase -translate-y-px flex-shrink-0 tracking-wide leading-none border-l border-r border-neutral-200 shadow-sm top-0 left-1/2 -translate-x-1/2 px-3 pt-1 pb-2 bg-white text-gray-400 text-[0.65rem]">Our Amazing Sponsors</div>
    <div class="mx-auto max-w-5xl">
        <div class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3  gap-3 sm:gap-5 not-prose">
            <a href="https://m.do.co/c/dc19b9819d06" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/digital-ocean.svg" alt="DigitalOcean" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">DigitalOcean offers a simple and reliable cloud hosting solution that enables developers to get their website or application up and running quickly.</span>
        </a>
            <a href="https://laravel-news.com/?utm_source=devdojo.com" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/laravel-news.svg?image=laravel-news" alt="Laravel News" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">Laravel News keeps you up to date with everything Laravel. Everything from framework news to new community packages, Laravel tutorials, and more.</span>
        </a>
            <a href="https://github.com/thedevdojo/genesis" target="_blank" class="relative flex flex-col items-start justify-between p-6 overflow-hidden rounded-xl border border-neutral-200 bg-white group">
            <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-30"></span>
            <div class="flex justify-between items-center mb-4 w-full">
                <img src="https://cdn.devdojo.com/sponsors/genesis.svg" alt="Genesis" class="relative h-5 md:h-6">
                <span class="opacity-0 -translate-x-2 flex-shrink-0 group-hover:translate-x-0 py-1 px-2.5 text-[0.6rem] group-hover:opacity-100 transition-all ease-out duration-200 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center">
                    <span>View Website</span>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-3 h-3 translate-x-0.5"><path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /></svg>
                </span>
            </div>
            <span class="relative text-xs md:text-sm text-neutral-600">A Laravel Starter Kit that includes Authentication, User Dashboard, Edit Profile, and a set of UI Components.</span>
        </a>
        <a href="/sponsorship" target="_blank" class="hidden overflow-hidden relative flex-col justify-between items-start p-8 bg-white rounded-xl border sm:flex lg:hidden border-neutral-200">
        <span class="absolute inset-0 w-full h-full bg-white group-hover:bg-gray-50 group-hover:bg-opacity-50"></span>
        <svg class="relative mb-4 h-4 opacity-80 fill-current md:h-6 text-neutral-900" viewBox="0 0 57 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m.06.48 2.952 5.268V9h2.4V5.748L8.364.48H5.796L4.26 3.66 2.712.48H.06ZM7.65 5.784c0 1.98 1.571 3.396 3.48 3.396 1.991 0 3.527-1.44 3.527-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.503-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38S9.93 6.48 9.93 5.772c0-.72.48-1.38 1.224-1.38ZM15.552 2.544V6.06c0 1.896 1.188 3.12 3.048 3.12 1.908 0 3.12-1.284 3.12-3.12V2.544h-2.244v3.564c0 .54-.216 1.044-.828 1.044s-.852-.492-.852-1.044V2.544h-2.244ZM22.794 2.544V9h2.244V6.036c0-.876.42-1.308 1.248-1.308.204 0 .384.024.552.084l.216-2.244a1.333 1.333 0 0 0-.552-.108c-.756 0-1.284.456-1.56 1.128l-.072-1.044h-2.076ZM27.894 9h5.628V6.888h-3.228V.48h-2.4V9ZM34.18 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42Zm3.504-1.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38ZM41.962 5.532c0 1.584 1.164 3.168 2.832 3.168.72 0 1.332-.276 1.764-.864v.492c0 .744-.42 1.308-1.188 1.308-.36 0-.72-.144-.936-.444l-2.208.42c.492 1.26 1.872 1.812 3.144 1.812 2.064 0 3.432-1.344 3.432-3.36v-5.52h-2.076l-.048.708c-.456-.612-1.044-.888-1.8-.888-1.716 0-2.916 1.548-2.916 3.168Zm3.432-1.26c.696 0 1.176.588 1.176 1.26 0 .684-.48 1.26-1.176 1.26-.684 0-1.152-.612-1.152-1.26 0-.66.468-1.26 1.152-1.26ZM49.696 5.784c0 1.98 1.572 3.396 3.48 3.396 1.992 0 3.528-1.44 3.528-3.42 0-1.992-1.56-3.396-3.48-3.396-1.992 0-3.528 1.44-3.528 3.42ZM53.2 4.392c.78 0 1.224.672 1.224 1.38 0 .72-.468 1.38-1.224 1.38s-1.224-.672-1.224-1.38c0-.72.48-1.38 1.224-1.38Z" /></svg>
        <span class="relative text-xs md:text-sm text-neutral-600">Learn more about the DevDojo sponsorship program and see your logo here to get your brand in front of thousands of developers.</span>
    </a>
</div>    </div>
</div>
<p>LogicLoop also works with <a href="https://cloud.materialize.com/">Materialize Cloud</a>.</p>
<h3 id="starting-the-demo-project">Starting the demo project</h3>
<p>A quick summary of the steps from the "<a href="https://devdojo.com/bobbyiliev/how-to-use-dbt-with-materialize-and-redpanda">how to use dbt with Materialize and Redpanda</a>" tutorial that you need to take to get the project up and running are as follows:</p>
<pre><code class="hljsphp"><span class="hljs-comment"># Clone the repository:</span>
git <span class="hljs-keyword">clone</span> https:<span class="hljs-comment">//github.com/bobbyiliev/materialize-tutorials.git</span>

<span class="hljs-comment"># Access the directory:</span>
cd materialize-tutorials
git checkout lts
cd mz-user-reviews-dbt-demo

<span class="hljs-comment"># Start by running the Redpanda container:</span>
docker-compose up -d redpanda

<span class="hljs-comment"># Build the images:</span>
docker-compose build

<span class="hljs-comment"># Then pull all of the other Docker images:</span>
docker-compose pull

<span class="hljs-comment"># Finally, start all of the services:</span>
docker-compose up -d
</code></pre>
<p>Once all the services are running, you can run the following commands to configure the dbt part:</p>
<pre><code class="hljsmarkdown"><span class="hljs-section"># Install dbt:</span>
pip3 install dbt-core==1.1.0
pip3 install dbt-materialize==1.1.0
</code></pre>
<p>After that, with your favorite text editor, open the ~/.dbt/project.yml file and add the following lines:</p>
<pre><code class="hljspython">user_reviews:
  outputs:
    dev:
      type: materialize
      threads: <span class="hljs-number">1</span>
      host: localhost
      port: <span class="hljs-number">6875</span>
      user: materialize
      <span class="hljs-keyword">pass</span>: <span class="hljs-keyword">pass</span>
      dbname: materialize
      schema: analytics

  target: dev
</code></pre>
<p>Finally, we can use dbt to create materialized views on top of the 3 Redpanda/Kafka topics. To do so just run the following dbt command:</p>
<pre><code class="hljs">dbt debug
dbt run
dbt test
</code></pre>
<p>With that, all the materialized views are created and we can start using them in our LogicLoop account.</p>
<p>For more details on the above steps, please refer to the previous article:</p>
<blockquote>
<p><a href="https://devdojo.com/bobbyiliev/how-to-use-dbt-with-materialize-and-redpanda">How to use dbt with Materialize and Redpanda</a></p>
</blockquote>
<h2 id="overview">Overview</h2>
<p>There are three main things that we will be doing in this tutorial:</p>
<ol>
<li>First we will add Materialize as a data source to LogicLoop.</li>
<li>Then we will write an SQL rule that will check our <code>vipusersbadreviews</code> materialized view which contains the bad reviews left by VIP users.</li>
<li>Next we will create an action destination so that we can get a notification when the rule is triggered. That way we can stay on top of the bad reviews and make sure that our VIP users are taken care of.</li>
</ol>
<p>Thanks to LogicLoop we can have all this without writing any custom or integrations. And thanks to Materialize we can get the data we need in real-time.</p>
<h2 id="add-materialize-as-a-source-to-logicloop">Add Materialize as a source to LogicLoop</h2>
<p>Start by logging into LogicLoop and navigating to the "Data Sources" page and clicking on the "New Data Source" button.</p>
<p>Next, as Materialize is wire-compatible with Postgres, you can use LogicLoop's Postgres driver to connect to your Materialize instance.</p>
<p>After choosing the Postgres driver, you will need to enter the following information:</p>
<ul>
<li>Name: A descriptive name for the data source, e.g. "My Materialize instance".</li>
<li>Host: The hostname of your Materialize instance.</li>
<li>Port: The port that your Materialize instance is listening on. Usually <code>6875</code>.</li>
<li>User: The username you use to connect to your Materialize instance.</li>
<li>Password: The password you use to connect to your Materialize instance.</li>
<li>Database Name: set this to <code>materialize</code> as this is the default one.</li>
</ul>
<p>Finally, you can click the "Create" button to create the data source.</p>
<h2 id="add-an-action-destination">Add an action destination</h2>
<p>LogicLoop has a list of built-in action destinations that you can use like Slack, Webhooks, Email, and more.</p>
<p>This allows you to send different kinds of notifications based on the rule that is triggered.</p>
<p>To add a new action destination, navigate to the "Destinations" page and click on the "New Action Destination" button.</p>
<p>In there you can choose the type of action destination you want to create:</p>
<p><img src="https://imgur.com/I1eFPJM.png" alt="LogicLoop destinations"></p>
<p>For the sake of simplicity, for this demo, let's create an email action destination and set it to our email address. However, you can also send emails to your end-facing customers as described in the <a href="https://docs.logicloop.com/actions/destinations/email">documentation here</a>.</p>
<p>Once you have created the action destination, you can click on the "Save" button.</p>
<h2 id="create-a-rule">Create a rule</h2>
<p>Once the data source is created, you can create a rule that will check the <code>vipusersbadreviews</code> materialized view.</p>
<p>To do so, navigate to the "Rules" page and click on the "New Rule" button.</p>
<p>From the dropdown menu, select your Materialize data source:</p>
<p><img src="https://imgur.com/KoC5qAZ.png" alt="Materialize data source"></p>
<p>You will be able to see all the views that are available in your Materialize instance, which we created in the previous article.</p>
<p>In the SQL editor, you can write queries that will be run against the materialized views:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">SELECT</span> <span class="hljs-selector-tag">COUNT</span>(*) <span class="hljs-selector-tag">FROM</span> <span class="hljs-selector-tag">analytics</span><span class="hljs-selector-class">.vipusersbadreviews</span>;
</code></pre>
<p>Based on the query, we will be able to see how many bad reviews there are for the VIP users.</p>
<p>We can also generate different visualizations for the data:</p>
<p><img src="https://imgur.com/2dt82KU.png" alt="Total bad reviews counter"></p>
<p>Let's update the rule to look like this:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">SELECT</span> * <span class="hljs-selector-tag">FROM</span> <span class="hljs-selector-tag">analytics</span><span class="hljs-selector-class">.vipusersbadreviews</span> <span class="hljs-selector-tag">LIMIT</span> 10;
</code></pre>
<p>This will return the last 10 bad reviews that we have for the VIP users. Feel free to change the query to your liking and edit the visualization as well.</p>
<p>Finally, click on the "Save" button to create the rule.</p>
<p>Next, let's create an action so that we can get a notification when the rule is triggered.</p>
<h2 id="add-an-action">Add an action</h2>
<p>Once the rule is created, you can add an action to the rule. While on the "Rules" page, click on the "Add Action" button at the bottom of the page.</p>
<p>The action can be based on different kinds of conditions as follows:</p>
<p><img src="https://imgur.com/ZgHssJJ.png" alt="Action conditions"></p>
<p>Configure the action based on your needs and click on the "Save" button.</p>
<p>Next, enable the destination that you created earlier so that when the rule is triggered, you will get a notification.</p>
<p>Finally, click on the "Run" button to run the action:</p>
<p><img src="https://imgur.com/tOrXjIF.png" alt="Run an action"></p>
<p>Alternatively, you can also set the rule to run automatically based on a specific time interval. The intervals supported by LogicLoop are: Minutes (1-30), Hours, Days, Weeks.</p>
<p>Once you run the rule, you will get a notification for each time the rule is matched:</p>
<p><img src="https://imgur.com/yOcTMBm.png" alt="Notification"></p>
<p>For more information on the above steps, please refer to the documentation:</p>
<blockquote>
<p><a href="https://docs.logicloop.com/actions/actions">Actions documentation</a></p>
</blockquote>
<h2 id="conclusion">Conclusion</h2>
<p>That's it! You can now use the materialized views in your LogicLoop account. This is a great way to get real-time data visualizations and notifications for your business.</p>
<p>As a next step you can look into using <a href="https://materialize.com/temporal-filters">temporal filters</a> so that your Materialize views are only keeping the data for a specific time period rather than the entire data set. And also run your LogicLoop rules on a schedule of 1-minute intervals to get the latest data from Materialize.</p>
<h2 id="useful-links">Useful links</h2>
<ul>
<li><a href="https://materialize.com/docs/">Materialize documentation</a></li>
<li><a href="https://docs.logicloop.com/">LogicLoop documentation</a></li>
<li><a href="https://materialize.com/docs/integrations/dbt/">Materialize and dbt</a></li>
</ul>
<h2 id="community">Community</h2>
<p>If you have any questions or comments, please join the <a href="https://materialize.com/s/chat">Materialize Slack Community</a>!</p>
]]></description>
                                                            <category>docker</category>
                                            <category>linux</category>
                                            <category> data</category>
                                            <category>devops</category>
                                            <category>materialize</category>
                                            <category>streaming data</category>
                                            <category>dbt</category>
                                            <category>redpanda</category>
                                            <category>kafka</category>
                                            <category>logicloop</category>
                                                    <author><![CDATA[Bobby Iliev]]></author>
                <guid>https://devdojo.com/8663</guid>
                <pubDate>Fri, 06 May 2022 08:53:49 -0700</pubDate>
            </item>
            </channel>
</rss>
