<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title><![CDATA[ DevDojo ]]></title>
        <link><![CDATA[ https://devdojo.com/feed ]]></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-04-22 01:56:54</pubDate>

                    <item>
                <title><![CDATA[11 Best V0 Alternatives 2025: Real Reviews &amp; Pricing]]></title>
                <link>https://devdojo.com/rahulism/11-best-v0-alternatives-2025-real-reviews-pricing</link>
                <description><![CDATA[<h2 id="why-look-beyond-v0-by-vercel"><strong>Why Look Beyond V0 by Vercel?</strong></h2>
<p>V0 by Vercel is solid for generating React components, but what if you need more than just frontend UI?</p>
<p>Look, V0 does one thing really well - it takes your text prompts and spits out clean React components with Tailwind CSS. Perfect if you're building in the Vercel ecosystem and just need UI pieces.</p>
<p><strong>But here's what V0 can't do(as of now):</strong></p>
<ul>
<li>
<p>Build full applications with backend logic</p>
</li>
<li>
<p>Work outside the React/Next.js world</p>
</li>
<li>
<p>Handle databases, authentication, or server-side code</p>
</li>
<li>
<p>Generate mobile apps or desktop applications</p>
</li>
</ul>
<p>That's where these 11 alternatives come in. Some do everything V0 does but better. Others go way beyond UI generation to build complete applications.</p>
<p><strong>What you'll find in this guide:</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>
<ul>
<li>
<p>Real user experiences and honest reviews</p>
</li>
<li>
<p>Tools that actually build full apps (not just UI)</p>
</li>
<li>
<p>Alternatives for different tech stacks and use cases</p>
</li>
<li>
<p>Pricing breakdowns and when each tool makes sense</p>
</li>
</ul>
<p>Whether you're stuck in V0's limitations or just want something more powerful, let's find your perfect alternative.</p>
<p>Also read:</p>
<ul>
<li>
<p><a href="https://www.comarketer.dev/blog/best-wordpress-alternatives-top-picks">14 Best WordPress Alternatives in 2025 (Top Picks)</a></p>
</li>
<li>
<p><a href="https://www.comarketer.dev/blog/best-website-builders-for-seo">Best Website Builders for SEO in 2025: Top 15 Platforms Compared</a></p>
</li>
<li>
<p><a href="https://www.comarketer.dev/blog/top-15-website-builders-for-agencies">Top 15 Website Builders for Agencies in 2025 | Expert Comparison &amp; Reviews</a></p>
</li>
</ul>
<h2 id="toc-11-best-v0-alternatives-in-2025-real-user-reviews"><strong>11 Best V0 Alternatives in 2025 (Real User Reviews)</strong></h2>
<p>V0 by Vercel is solid for generating React components, but what if you need more than just frontend UI?</p>
<p>Look, V0 does one thing really well - it takes your text prompts and spits out clean React components with Tailwind CSS. Perfect if you're building in the Vercel ecosystem and just need UI pieces.</p>
<p>But here's what V0 can't do:</p>
<ul>
<li>
<p>Build full applications with backend logic</p>
</li>
<li>
<p>Work outside the React/Next.js world</p>
</li>
<li>
<p>Handle databases, authentication, or server-side code</p>
</li>
<li>
<p>Generate mobile apps or desktop applications</p>
</li>
</ul>
<p>That's where these 11 alternatives come in. Some do everything V0 does but better. Others go way beyond UI generation to build complete applications.</p>
<p><strong>What you'll find in this guide:</strong></p>
<ul>
<li>
<p>Real user experiences and honest reviews</p>
</li>
<li>
<p>Tools that actually build full apps (not just UI)</p>
</li>
<li>
<p>Alternatives for different tech stacks and use cases</p>
</li>
<li>
<p>Pricing breakdowns and when each tool makes sense</p>
</li>
</ul>
<p>Whether you're stuck in V0's limitations or just want something more powerful, let's find your perfect alternative.</p>
<h2 id="toc-0-comarketer-dev"><strong>0. Comarketer.dev</strong></h2>
<p>![](https://comarketer-cms.vercel.app//api/media/file/image5-3.png align="left")</p>
<p><strong>What</strong> <a href="https://www.comarketer.dev/"><strong>it</strong></a> <strong>is:</strong> AI-powered website builder with chat-based interface that generates professional, SEO-optimized websites. Combines prompt-based creation with visual editing and code access for maximum flexibility.</p>
<p><strong>Pros:</strong></p>
<ul>
<li>
<p>Generates complete websites from simple prompts in seconds</p>
</li>
<li>
<p>Chat interface feels natural - like talking to a web designer</p>
</li>
<li>
<p>Visual editor for non-technical users + full code editor for developers</p>
</li>
<li>
<p>Built-in SEO analysis and optimization (auto-generates sitemap.xml, robots.txt)</p>
</li>
<li>
<p>One-click Vercel deployment with free custom domain hosting</p>
</li>
<li>
<p>Payload CMS integration for dynamic content management</p>
</li>
<li>
<p>Mobile-responsive designs out of the box</p>
</li>
<li>
<p>Pay-as-you-use model - no subscription traps</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li>
<p>Limited to website building (no full-stack apps like V0)</p>
</li>
<li>
<p>Not designed for complex web applications or internal tools</p>
</li>
<li>
<p>Smaller community compared to established builders</p>
</li>
<li>
<p>Credits-based system might not suit heavy users</p>
</li>
<li>
<p>Less suitable for developers wanting complete code control from scratch</p>
</li>
</ul>
<p><strong>Pricing:</strong></p>
<ul>
<li>
<p>Free: 20 credits (builds ~2 complete websites)</p>
</li>
<li>
<p>Pay-as-you-go: $15 for 100 credits</p>
</li>
<li>
<p>No monthly subscriptions or hidden fees</p>
</li>
</ul>
<p>Reality check: Transparent pricing with no token surprises. Two professional websites for free, then predictable costs.</p>
<p><strong>Best for:</strong></p>
<ul>
<li>
<p>Founders and agencies needing professional business websites fast</p>
</li>
<li>
<p>Marketing-focused sites that need to rank in search</p>
</li>
<li>
<p>Teams wanting AI speed without sacrificing customization</p>
</li>
<li>
<p>Users who prefer chat-based editing over drag-and-drop</p>
</li>
<li>
<p>Projects requiring both visual editing and code access</p>
</li>
</ul>
<h2 id="toc-1-bolt-new"><strong>1. Bolt.new</strong></h2>
<p>![](https://comarketer-cms.vercel.app//api/media/file/image2-3.png align="left")</p>
<p><strong>What</strong> <a href="https://www.bolt.new/"><strong>it</strong></a> <strong>is:</strong> Browser-based AI that builds full-stack React apps with backends using Claude 3.5 Sonnet. No local setup needed - everything runs in WebContainers.</p>
<p><strong>Pros:</strong></p>
<ul>
<li>
<p>Builds complete apps (frontend + backend + database) from simple prompts</p>
</li>
<li>
<p>Supports React, Next.js, Vue, and Svelte with npm packages</p>
</li>
<li>
<p>One-click deployment to Netlify</p>
</li>
<li>
<p>Great for rapid prototyping and demos</p>
</li>
<li>
<p>Works entirely in the browser</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li>
<p>Token consumption is insane - users burned 20M tokens fixing single auth issues</p>
</li>
<li>
<p>Rewrites entire files instead of targeted fixes, breaking working code</p>
</li>
<li>
<p>Projects break after 1,000 lines of code - AI starts hallucinating</p>
</li>
<li>
<p>Code mysteriously disappears during builds</p>
</li>
<li>
<p>Poor debugging - can't make incremental fixes</p>
</li>
</ul>
<p><strong>Pricing:</strong></p>
<ul>
<li>
<p>Free: 1M tokens/month (burns through fast)</p>
</li>
<li>
<p>Pro: $25/month (10M tokens)</p>
</li>
<li>
<p>Teams: $30/month (10M tokens/member)</p>
</li>
<li>
<p>Enterprise: Custom.&nbsp;</p>
</li>
</ul>
<p>Reality check: Complex projects eat 200k+ tokens per prompt, hitting $1,000+ monthly costs</p>
<p><strong>Best for:</strong></p>
<ul>
<li>
<p>Quick landing pages and demos</p>
</li>
<li>
<p>Learning new frameworks</p>
</li>
<li>
<p>Proof-of-concepts under 1,000 lines</p>
</li>
<li>
<p>Non-developers building simple apps</p>
</li>
</ul>
<h2 id="toc-2-lovable"><strong>2. Lovable</strong></h2>
<p>![](https://comarketer-cms.vercel.app//api/media/file/image14-2.png align="left")</p>
<p><strong>What</strong> <a href="https://www.lovable.dev/"><strong>it</strong></a> <strong>is:</strong> Chat-based AI that builds full-stack apps with Supabase integration, GitHub sync, and one-click deployment. Claims 20x faster development.</p>
<p><strong>Pros:</strong></p>
<ul>
<li>
<p>Simple chat interface - describe app ideas in plain English</p>
</li>
<li>
<p>Generates frontend + backend + database automatically</p>
</li>
<li>
<p>Seamless Supabase integration for real-time features</p>
</li>
<li>
<p>Direct GitHub integration with version control</p>
</li>
<li>
<p>You own the generated code completely</p>
</li>
<li>
<p>Message-based pricing instead of confusing tokens</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li>
<p>Can't edit code directly in the platform - stuck with AI suggestions</p>
</li>
<li>
<p>Limited to 5 messages daily on the free plan</p>
</li>
<li>
<p>Complex apps still require technical knowledge to prompt effectively</p>
</li>
<li>
<p>Backend generation sometimes creates overcomplicated architectures</p>
</li>
<li>
<p>Real-time collaboration is still in beta</p>
</li>
</ul>
<p><strong>Pricing:</strong></p>
<ul>
<li>
<p>Free: 5 messages/day, 30/month max</p>
</li>
<li>
<p>Pro: $20/month (200 messages + GitHub sync)</p>
</li>
<li>
<p>Business: $50/month (unlimited messages + team features)</p>
</li>
</ul>
<p><strong>Best for:</strong></p>
<ul>
<li>
<p>Non-technical founders building MVPs</p>
</li>
<li>
<p>Startup teams needing quick prototypes</p>
</li>
<li>
<p>Apps requiring Supabase backend features</p>
</li>
<li>
<p>Teams that prefer chat over code editing</p>
</li>
</ul>
<h2 id="toc-3-ui-bakery"><strong>3. UI Bakery</strong></h2>
<p>![](https://comarketer-cms.vercel.app//api/media/file/image10-2.png align="left")</p>
<p><strong>What</strong> <a href="https://www.uibakery.io/"><strong>it</strong></a> <strong>is:</strong> Low-code platform focused on internal business tools with AI assistance. Built for connecting databases and creating admin panels, dashboards, and CRUD apps.</p>
<p><strong>Pros:</strong></p>
<ul>
<li>
<p>Excellent database connectivity (PostgreSQL, MySQL, MongoDB, Google Sheets, Airtable)</p>
</li>
<li>
<p>75+ pre-built components for business apps</p>
</li>
<li>
<p>Strong security and compliance features</p>
</li>
<li>
<p>Visual drag-and-drop interface with custom JavaScript support</p>
</li>
<li>
<p>Great customer support - users consistently praise responsiveness</p>
</li>
<li>
<p>Self-hosted options available</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li>
<p>Requires some technical knowledge - not truly no-code</p>
</li>
<li>
<p>Learning curve for complex app building</p>
</li>
<li>
<p>AI features are limited compared to full AI builders</p>
</li>
<li>
<p>Pricing gets expensive for larger teams</p>
</li>
<li>
<p>More suited for internal tools than consumer apps</p>
</li>
</ul>
<p><strong>Pricing:</strong></p>
<ul>
<li>
<p>Free: Unlimited apps, basic features</p>
</li>
<li>
<p>Standard: ~$10/month (10 apps, unlimited data sources)</p>
</li>
<li>
<p>Business: ~$25/month (unlimited apps + collaboration)</p>
</li>
<li>
<p>Enterprise: Custom pricing</p>
</li>
</ul>
<p><strong>Best for:</strong></p>
<ul>
<li>
<p>Internal business tools and admin panels</p>
</li>
<li>
<p>Companies with existing databases</p>
</li>
<li>
<p>Teams building CRUD applications</p>
</li>
<li>
<p>Organizations requiring compliance and security</p>
</li>
</ul>
<h2 id="toc-4-cursor"><strong>4. Cursor</strong></h2>
<p>![](https://comarketer-cms.vercel.app//api/media/file/image7-3.png align="left")</p>
<p><strong>What it is:</strong> AI-powered VS Code fork with Claude 3.5 Sonnet, GPT-4, and other models built directly into the editor. Acts as an AI coding partner.</p>
<p><strong>Pros:</strong></p>
<ul>
<li>
<p>Familiar VS Code interface with powerful AI integration</p>
</li>
<li>
<p>Multi-file code generation with Composer mode</p>
</li>
<li>
<p>Smart context awareness through codebase indexing</p>
</li>
<li>
<p>.cursor rules for project-specific AI behavior</p>
</li>
<li>
<p>Tab completion that's faster and smarter than Copilot</p>
</li>
<li>
<p>Works with existing VS Code extensions</p>
</li>
</ul>
<p><strong>Cons: The interface</strong> can get cluttered with AI buttons and popups</p>
<ul>
<li>
<p>AI suggestions range from brilliant to completely wrong</p>
</li>
<li>
<p>Recent updates caused crashes and performance issues</p>
</li>
<li>
<p>Customer support is reportedly slow or non-existent</p>
</li>
<li>
<p>Usage limits on "fast" vs ."slow" requests create confusion</p>
</li>
</ul>
<p><strong>Pricing:</strong></p>
<ul>
<li>
<p>Free: 200 completions, 50 fast requests with GPT-4</p>
</li>
<li>
<p>Pro: $20/month (unlimited completions, 500 fast requests)</p>
</li>
<li>
<p>Business: $40/month (centralized billing, admin controls)</p>
</li>
<li>
<p>Ultra: $200/month</p>
</li>
</ul>
<p><strong>Best for:</strong></p>
<ul>
<li>
<p>Developers already using VS Code</p>
</li>
<li>
<p>Complex refactoring across multiple files</p>
</li>
<li>
<p>Teams wanting AI assistance without changing workflows</p>
</li>
<li>
<p>Projects requiring precise context control</p>
</li>
</ul>
<h2 id="toc-5-replit"><strong>5. Replit</strong></h2>
<p>![](https://comarketer-cms.vercel.app//api/media/file/image15-3.png align="left")</p>
<p><strong>What it is:</strong> Cloud IDE with Replit Agent - an AI that builds complete apps from prompts. Everything runs in the browser with instant deployment.</p>
<p><strong>Pros:</strong></p>
<ul>
<li>
<p>Zero setup - code from any device with internet</p>
</li>
<li>
<p>Agent builds complete apps from single prompts</p>
</li>
<li>
<p>Supports 50+ programming languages</p>
</li>
<li>
<p>Built-in collaboration and version control</p>
</li>
<li>
<p>Instant deployment and sharing</p>
</li>
<li>
<p>Great for learning and education</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li>
<p>Agent pricing is confusing - charged per "checkpoint"</p>
</li>
<li>
<p>Performance issues and reliability problems were reported</p>
</li>
<li>
<p>Limited local development options</p>
</li>
<li>
<p>Support team is unresponsive according to users</p>
</li>
<li>
<p>Usage limits feel restrictive on paid plans</p>
</li>
<li>
<p>The agent sometimes fails on complex requests</p>
</li>
</ul>
<p><strong>Pricing:</strong></p>
<ul>
<li>
<p>Free: Basic features, limited AI usage</p>
</li>
<li>
<p>Core: $20/month ($25 credits included, unlimited private repos)</p>
</li>
<li>
<p>Teams: $35/month per user ($40 credits included, enhanced security, team features)</p>
</li>
</ul>
<p>Pay as you go for more AI credits for agents.&nbsp;</p>
<p><strong>Best for:</strong></p>
<ul>
<li>
<p>Students and coding beginners</p>
</li>
<li>
<p>Quick prototypes and demos</p>
</li>
<li>
<p>Collaborative coding projects</p>
</li>
<li>
<p>Developers who can't install local tools</p>
</li>
</ul>
<h2 id="toc-6-windsurf"><strong>6. Windsurf</strong></h2>
<p>![](https://comarketer-cms.vercel.app//api/media/file/image6-3.png align="left")</p>
<p><strong>What it is:</strong> AI-native IDE by Codeium with "Cascade" agent that automatically understands context and runs commands. Built on VS Code with a cleaner interface.</p>
<p><strong>Pros:</strong></p>
<ul>
<li>
<p>Completely free for individual use (no usage limits)</p>
</li>
<li>
<p>Cascade agent works like Cursor's Composer, but launched first</p>
</li>
<li>
<p>Cleaner, more intuitive UI than Cursor</p>
</li>
<li>
<p>Writes code changes to disk before approval</p>
</li>
<li>
<p>Strong privacy focus - no training on user data</p>
</li>
<li>
<p>Multiple AI models available (Claude, GPT-4, etc.)</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li>
<p>Cascade feature can be unreliable - sometimes fails to write files</p>
</li>
<li>
<p>Less mature ecosystem compared to Cursor</p>
</li>
<li>
<p>Some users report connection issues and bugs</p>
</li>
<li>
<p>Limited documentation for advanced features</p>
</li>
<li>
<p>The recent acquisition by OpenAI ($3B)is&nbsp; creating uncertainty</p>
</li>
</ul>
<p><strong>Pricing:</strong></p>
<ul>
<li>
<p>Individual: Completely free forever</p>
</li>
<li>
<p>Pro: $15/month per seat</p>
</li>
<li>
<p>Teams: $30/user/month</p>
</li>
<li>
<p>Enterprise: Custom pricing</p>
</li>
</ul>
<p><strong>Best for:</strong></p>
<ul>
<li>
<p>Developers wanting free AI coding assistance</p>
</li>
<li>
<p>Teams on tight budgets</p>
</li>
<li>
<p>Users preferring simpler, cleaner interfaces</p>
</li>
<li>
<p>Privacy-conscious developers</p>
</li>
</ul>
<h2 id="toc-7-magic-patterns"><strong>7. Magic Patterns</strong></h2>
<p>![](https://comarketer-cms.vercel.app//api/media/file/image12-3.png align="left")</p>
<p><strong>What it is:</strong> AI-powered UI design tool for product teams that generates React/Figma components from text prompts, with design system integration and team collaboration.</p>
<p><strong>Pros:</strong></p>
<ul>
<li>
<p>Generates production-ready React code and Figma exports</p>
</li>
<li>
<p>Integrates with existing design systems perfectly</p>
</li>
<li>
<p>Real-time multiplayer collaboration on an infinite canvas</p>
</li>
<li>
<p>Chrome extension captures UI from any website</p>
</li>
<li>
<p>Strong focus on matching existing brand guidelines</p>
</li>
<li>
<p>Y Combinator backed with 100k+ components generated</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li>
<p>Works best for smaller components, not full applications</p>
</li>
<li>
<p>Limited to UI generation - no backend capabilities</p>
</li>
<li>
<p>Still a relatively new platform (launched in 2023)</p>
</li>
<li>
<p>Pricing not transparent - requires contacting sales</p>
</li>
<li>
<p>More focused on design iteration than app building</p>
</li>
</ul>
<p><strong>Pricing:</strong></p>
<ul>
<li>
<p>Free: Basic features for individuals</p>
</li>
<li>
<p>Hobby: $19/month (team collaboration, unlimited exports)</p>
</li>
<li>
<p>Pro: $75/month (3.5x limits and more features)</p>
</li>
<li>
<p>Enterprise: Custom pricing for large teams</p>
</li>
</ul>
<p><strong>Best for:</strong></p>
<ul>
<li>
<p>Product teams iterating on UI designs</p>
</li>
<li>
<p>Designers working with existing design systems</p>
</li>
<li>
<p>Teams needing rapid component prototyping</p>
</li>
<li>
<p>Companies wanting branded, consistent UI elements</p>
</li>
</ul>
<h2 id="toc-8-builder-io"><strong>8. Builder.io</strong></h2>
<p>![](https://comarketer-cms.vercel.app//api/media/file/image1-3.png align="left")</p>
<p><strong>What it is:</strong> Visual development platform with AI-powered design-to-code capabilities, drag-and-drop CMS, and enterprise features. Converts Figma designs to production code.</p>
<p><strong>Pros:</strong></p>
<ul>
<li>
<p>AI converts Figma designs to clean React/Vue/Angular code</p>
</li>
<li>
<p>Visual CMS lets non-developers build pages independently</p>
</li>
<li>
<p>Integrates with existing codebases and design systems</p>
</li>
<li>
<p>Built-in A/B testing and personalization features</p>
</li>
<li>
<p>Enterprise-grade with SSO, custom roles, and premium support</p>
</li>
<li>
<p>Works with any framework or backend</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li>
<p>Steeper learning curve than pure no-code tools</p>
</li>
<li>
<p>It can be expensive for smaller teams</p>
</li>
<li>
<p>Visual editor crashes reported by some users</p>
</li>
<li>
<p>Requires developer setup for advanced integrations</p>
</li>
<li>
<p>More complex than simple AI builders</p>
</li>
</ul>
<p><strong>Pricing:</strong></p>
<ul>
<li>
<p>Free: 1 space, basic features, 10k visual views</p>
</li>
<li>
<p>Growth: $24/user/month (500 agent credits, team features)</p>
</li>
<li>
<p>Enterprise: Custom pricing (unlimited everything)</p>
</li>
</ul>
<p>Has Pay-as-you-go for more usage</p>
<p><strong>Best for:</strong></p>
<ul>
<li>
<p>Enterprise teams with existing codebases</p>
</li>
<li>
<p>Marketing teams need developer independence</p>
</li>
<li>
<p>Companies with complex design systems</p>
</li>
<li>
<p>Teams wanting visual editing with code control</p>
</li>
</ul>
<h2 id="toc-9-a0-dev"><strong>9. a0.dev</strong></h2>
<p>![](https://comarketer-cms.vercel.app//api/media/file/image3-3.png align="left")</p>
<p><strong>What it is:</strong> Y Combinator-backed AI platform that builds complete React Native mobile apps from text descriptions, with instant iOS builds and App Store deployment.</p>
<p><strong>Pros:</strong></p>
<ul>
<li>
<p>Generates full React Native apps for iOS/Android</p>
</li>
<li>
<p>Instant iOS build process (seconds from code to installable app)</p>
</li>
<li>
<p>One-click App Store submission with Apple provisioning handled</p>
</li>
<li>
<p>Includes backend logic, payments, and monetization features</p>
</li>
<li>
<p>AI agent handles debugging and app optimization</p>
</li>
<li>
<p>Built by experienced app developers (7 years of mobile experience)</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li>
<p>Very new platform (YC W25 batch)</p>
</li>
<li>
<p>Limited track record and user reviews</p>
</li>
<li>
<p>Mobile-only focus (no web apps)</p>
</li>
<li>
<p>Pricing structure not fully clear</p>
</li>
<li>
<p>AI-generated apps may need manual refinement</p>
</li>
<li>
<p>App Store approval still depends on Apple's policies</p>
</li>
</ul>
<p><strong>Pricing:</strong></p>
<ul>
<li>
<p>Free: Basic plan for hobby projects (1 app build)</p>
</li>
<li>
<p>Starter: $19/month (10-50x higher limits, unlimited builds, payments)</p>
</li>
<li>
<p>Production: $99/mo ( Higher priority builds, 2 build concurrencies, and updates for up to 50,000 MAUs)</p>
</li>
<li>
<p>Enterprise: Custom pricing for scale</p>
</li>
</ul>
<p><strong>Best for:</strong></p>
<ul>
<li>
<p>Entrepreneurs wanting to launch mobile apps quickly</p>
</li>
<li>
<p>Developers focused on mobile-first products</p>
</li>
<li>
<p>Teams needing React Native expertise without a learning curve</p>
</li>
<li>
<p>Startups wanting to test mobile app ideas fast</p>
</li>
</ul>
<h2 id="which-v0-alternative-should-you-choose"><strong>Which V0 Alternative Should You Choose?</strong></h2>
<p><strong>For Full-Stack Apps:</strong> Bolt.new or Lovable if you need complete applications with backends.</p>
<p><strong>For Enterprise Teams:</strong> Builder.io or UI Bakery for existing codebases and team collaboration</p>
<p><strong>For Mobile Apps:</strong> a0.dev for React Native or Replit for cross-platform development</p>
<p><strong>For Coding Assistance:</strong> Cursor for VS Code users or Windsurf for free AI coding help</p>
<p><strong>For UI Design:</strong> Magic Patterns for design systems or ReactAI for simple React components</p>
<p><strong>For Marketing Sites:</strong> Comarketer for SEO-focused landing pages</p>
<p>The truth is, no single tool replaces V0 perfectly. Each excels in different areas. Choose based on your specific needs: full-stack development, mobile apps, enterprise requirements, or simple UI generation.</p>
<p><strong>Bottom line:</strong> V0 is great for React component generation, but these alternatives offer more specialized solutions for specific use cases. Try a few to see what fits your workflow best.</p>
<h2 id="what-next"><strong>What next?</strong>&nbsp;</h2>
<p>Now that you know the 11 best V0 alternatives, here's your action plan:</p>
<p><strong>1. Pick 2-3 tools</strong> that match your specific needs from the list above</p>
<p><strong>2. Start with free trials</strong> - Most tools offer free tiers or trial periods</p>
<p><strong>3. Test with your actual project</strong> - Don't just play around, use it for real work</p>
<p><strong>4. Compare the results</strong> - Which one saves you the most time? Which fits your workflow?</p>
<p><strong>5. Consider the costs</strong> - Factor in pricing as your usage scales</p>
<p>Remember, the best tool is the one you'll actually use consistently. Don't get caught up in feature lists - focus on what solves your specific problems.</p>
<p><strong>Thanks for reading!</strong></p>
<p>Go grab those free trials and see what works for your exact use case. Each tool has its strengths, so test them with your real projects to find your perfect match.</p>
<p>The AI development space moves fast, so bookmark this guide and check back for updates.&nbsp;</p>
<p>Good luck building!</p>
]]></description>
                                                            <category> coding</category>
                                            <category>v0</category>
                                            <category>vibe code</category>
                                                    <author><![CDATA[ ]]></author>
                <guid>https://devdojo.com/13570</guid>
                <pubDate>Fri, 03 Oct 2025 11:05:42 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[8 AI Developer Tools for Faster &amp; Smarter Development 👨‍💻🚀]]></title>
                <link>https://devdojo.com/madzadev/8-ai-developer-tools-for-faster-smarter-development</link>
                <description><![CDATA[<p>Artificial intelligence has been transforming the way software developers build applications by providing them with new ways to simplify repetitive tasks, increase their productivity, and create smarter programs.</p>
<p>But the problem is that there are so many AI-powered tools coming out that deciding which ones to use and how to integrate them into your workflow might take a long time and be very frustrating.</p>
<p>This article is my personal selection from 8 AI developer tools that have recently caught my eye to speed up the development process, simplify the workflows, and increase the quality of the code.</p>
<p>These tools cover a wide range of functionalities, from AI reviews, bug fixes, code generation, and API monitoring to API testing, code snippet management, color palette creators, and more.</p>
<p>Every product is briefly described, and I’ve included its main functionalities and the direct links to be able to get a sense of which set of features might be what you are looking for.</p>
<p>Let's take a closer look - I really hope this selection will be useful to you in your new projects!</p>
<ol>
<li><a href="https://www.coderabbit.ai">CodeRabbit</a> – AI-powered code reviews and bug fixes</li>
</ol>
<hr>
<p>Coderabbit is an AI-powered code review assistant that automates code quality checks, security scans, and performance optimizations, providing real-time feedback in your pull requests.</p>
<p>It works as a smart coding partner that takes into account your coding virtues and, learning from them, makes new suggestions to help you write clean, safe, and efficient code in less time.</p>
<p><a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn02m09lmirq3abq75nbl.jpg"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn02m09lmirq3abq75nbl.jpg" alt="CodeRabbit"></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><strong>Some of the most awesome features include:</strong></p>
<p><strong>🐇 Instant AI-assisted code reviews:</strong> Automatically analyze pull requests using deep code understanding to detect bugs, security issues, and code errors before merging.</p>
<p><strong>🤖 Conversational AI reviewer:</strong> Ask questions, get explanations, and receive practical suggestions for best practices just like collaborating with a smart team member.</p>
<p><strong>🚀 Seamless integration:</strong> Supports various version control platforms such as GitHub, GitLab, Bitbucket, and it can also be integrated into IDEs and code editors like VS Code.</p>
<p><strong>🔒 Privacy-first:</strong> No data retention and enterprise-grade encryption are some of the main security measures that contribute in keeping your code safe.</p>
<p><strong>📊 Developer insights:</strong> It tracks user code metrics and provides analytics dashboards to improve the productivity and code quality over time.</p>
<p><a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9vdkb23ls18s8pk9y1yo.png"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9vdkb23ls18s8pk9y1yo.png" alt="CodeRabbit"></a></p>
<p>Say goodbye to slow, manual code reviews and accelerate your workflow with CodeRabbit! <a href="https://www.coderabbit.ai">Try it for free</a> and experience how AI can boost your coding game starting today!</p>
<p><strong>🌎 Website Link:</strong> <a href="https://www.coderabbit.ai">https://www.coderabbit.ai</a></p>
<p>Thanks to the CodeRabbit team for sponsoring this article!</p>
<ol start="2">
<li><a href="https://bolt.new/">Bolt</a> - Build full-stack web apps using AI prompts</li>
</ol>
<hr>
<p><a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F83niu9kzpyaubqqlic14.png"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F83niu9kzpyaubqqlic14.png" alt="Bolt"></a></p>
<p>Bolt is a co-pilot powered by AI that lets developers create, edit, and deploy web applications in no time by simply describing the desired output in the AI prompt with natural language.</p>
<p><strong>Key features &amp; why to use it:</strong></p>
<ul>
<li>Creates full-stack web applications on the spot with simple language prompts.</li>
<li>Browser-based IDE with the functionality of live code editing and testing environment.</li>
<li>There is a built-in one-click deployment for fast app sharing.</li>
</ul>
<p><strong>🌎 Website Link:</strong> <a href="https://bolt.new/">https://bolt.new/</a></p>
<ol start="3">
<li><a href="https://www.trae.ai/">Trae</a> - Manage software lifecycle &amp; releases</li>
</ol>
<hr>
<p><a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fanl42wg7lzlhy7k3ntxb.jpg"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fanl42wg7lzlhy7k3ntxb.jpg" alt="Trae"></a></p>
<p>Trae is an AI-powered development orchestration tool, which takes the burden of manual work off the developers and executes automated, error-free software releases.</p>
<p><strong>Key features &amp; why to use it:</strong></p>
<ul>
<li>Intelligent scheduling enables the automation of release workflows.</li>
<li>Detailed tracking and monitoring statistics of deployments for analytics.</li>
<li>Support for popular CI/CD tools and platforms for extra features and productivity.</li>
</ul>
<p><strong>🌎 Website Link:</strong> <a href="https://www.trae.ai/">https://www.trae.ai/</a></p>
<ol start="4">
<li><a href="https://treblle.com/">Treblle</a> - Monitor API performance &amp; analytics</li>
</ol>
<hr>
<p><a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fitz2oab4y4lra1yzlwsh.png"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fitz2oab4y4lra1yzlwsh.png" alt="Treblle"></a></p>
<p>Treblle offers continuous API monitoring service that is supported by AI insights, thus saving the developer's time making him/her able to focus on the API's optimization and efficiency.</p>
<p><strong>Key features &amp; why to use it:</strong></p>
<ul>
<li>Continuous API usage monitoring and error recording to improve performance.</li>
<li>Detection of irregular activities in the API plus reporting is carried out by AI tools.</li>
<li>Wide range of API frameworks allows for further integration and features.</li>
</ul>
<p><strong>🌎 Website Link:</strong> <a href="https://treblle.com/">https://treblle.com</a></p>
<ol start="5">
<li><a href="https://codesnippets.ai/">CodeSnippets</a> - Create &amp; access reusable code snippets</li>
</ol>
<hr>
<p><a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkld89cysurcikdlgs1kn.png"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkld89cysurcikdlgs1kn.png" alt="CodeSnippets"></a></p>
<p>CodeSnippets is an artificial intelligence-powered platform that enables developers to create, debug, and manage reusable code snippets, giving them more time to focus on app logic.</p>
<p><strong>Key features &amp; why to use it:</strong></p>
<ul>
<li>AI-assisted code snippet generation and debugging directly within Visual Studio Code.</li>
<li>Comes with secure snippet library with quick search and sharing capabilities.</li>
<li>Supports multiple AI models and integrates seamlessly into developer workflows.</li>
</ul>
<p><strong>🌎 Website Link:</strong> <a href="https://codesnippets.ai/">https://codesnippets.ai</a></p>
<ol start="6">
<li><a href="https://httpie.io/">HTTPie</a> - <strong>Simplify REST, GraphQL &amp; HTTP testing</strong></li>
</ol>
<hr>
<p><a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj52wa522w3ghqwkrmvb2.png"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj52wa522w3ghqwkrmvb2.png" alt="HTTPie"></a></p>
<p>HTTPie is a human-friendly command-line and GUI client designed for testing, debugging, and interacting with APIs and HTTP servers efficiently with expressive syntax.</p>
<p><strong>Key features &amp; why to use it:</strong></p>
<ul>
<li>Intuitive, natural syntax with formatted and colorized output.</li>
<li>Supports JSON, forms, file uploads, authentication, proxies, and custom headers.</li>
<li>Cross-platform support with plugins for extended functionality.</li>
</ul>
<p><strong>🌎 Website Link:</strong> <a href="https://httpie.io/">https://httpie.io</a></p>
<ol start="7">
<li><a href="https://taipy.io/">Taipy</a> - Build complex data-driven applications</li>
</ol>
<hr>
<p><a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fynki8jvc71jnpregd7uu.png"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fynki8jvc71jnpregd7uu.png" alt="Taipy"></a></p>
<p>Taipy allows developers to build engaging data-driven web applications and dashboards with simplified tools for both front-end and back-end which are intuitive even without web design knowledge.</p>
<p><strong>Key features &amp; why to use it:</strong></p>
<ul>
<li>Python API for creating the GUI pages and dashboards to fit the users needs and preferences.</li>
<li>Scenario and pipeline management for backend workflows and dataflows.</li>
<li>Capable of large datasets, real-time updates, and multiple user environments.</li>
</ul>
<p><strong>🌎 Website Link:</strong> <a href="https://taipy.io/">https://taipy.io/</a></p>
<ol start="8">
<li><a href="https://supercolorpalette.com/">Super Color Palette</a> - Generate custom color palettes</li>
</ol>
<hr>
<p><a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9kmj0xm6yer2lqan2ski.png"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9kmj0xm6yer2lqan2ski.png" alt="Super Color Palette"></a></p>
<p>Super Color Palette is an online tool by which users can create, edit and export various multi-color palettes just by changing the hue, saturation, lightness, and other properties of each shade.</p>
<p><strong>Key features &amp; why to use it:</strong></p>
<ul>
<li>Generate palettes with different color harmony modes and modify the properties of the entire palette.</li>
<li>Users are able to check color contrasts with WCAG compliance accessibility metrics.</li>
<li>Download palettes in different file types and save palettes for simple management.</li>
</ul>
<p><strong>🌎 Website Link:</strong> <a href="https://supercolorpalette.com/">https://supercolorpalette.com/</a></p>
<h3 id="did-you-like-the-resources-here-is-more">Did you like the resources? Here is more 👇</h3>
<p>Join 6,000+ others to receive the best DEV resources, tools, productivity tips, and career growth advice I discover by subscribing to <a href="https://madzadev.substack.com/">my newsletter</a>!</p>
<p><a href="https://madzadev.substack.com/"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5nb15k9rlvy91bc7yd4c.png" alt="The Developer Toolbox"></a></p>
<p>Also, connect with me on <a href="https://twitter.com/madzadev">Twitter</a>, <a href="https://www.linkedin.com/in/madzadev/">LinkedIn</a>, and <a href="https://github.com/madzadev">GitHub</a>!</p>
<p>Writing has always been my passion, and it gives me pleasure to help and inspire people. If you want to get featured or partner up, feel free to <a href="https://www.madza.dev/contact">get in touch</a>!</p>
]]></description>
                                                                    <author><![CDATA[ ]]></author>
                <guid>https://devdojo.com/13563</guid>
                <pubDate>Tue, 30 Sep 2025 01:01:06 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[The 5-Minute Kubernetes Cluster Health Check]]></title>
                <link>https://devdojo.com/devopsdaily/the-5-minute-kubernetes-cluster-health-check</link>
                <description><![CDATA[<h2 id="tldr">TLDR</h2>
<p>You can check your Kubernetes cluster's health in under 5 minutes using five key commands: checking node status, monitoring resource usage, reviewing pod health across namespaces, investigating problem pods, and examining cluster events. This quick routine helps catch issues before they escalate into critical problems.</p>
<p>Kubernetes is great until it's not. One bad node, a pod stuck in CrashLoopBackOff, or a resource spike can ruin your day. The good news? You don't need to spend an hour digging through dashboards to spot trouble early. With a few quick commands, you can get a solid read on your cluster's health in under 5 minutes.</p>
<p>Here's how to do it effectively.</p>
<h2 id="make-sure-your-nodes-are-happy">Make Sure Your Nodes Are Happy</h2>
<p>Start by checking the overall status of your cluster nodes. This gives you the foundation-level health of your infrastructure.</p>
<pre><code class="hljsjavascript">kubectl <span class="hljs-keyword">get</span> nodes -o wide
</code></pre>
<p>This command displays all nodes in your cluster along with their detailed information. You'll see each node's status, roles, age, version, internal and external IPs, OS image, kernel version, and container runtime.</p>
<p>What you want to see:</p>
<ul>
<li><strong>STATUS</strong> should be <code>Ready</code> for all nodes</li>
<li>No mystery nodes suddenly showing up in your cluster</li>
<li>Roles, IPs, and ages that make sense for your environment</li>
</ul>
<p>If you spot <code>NotReady</code>, that's your cue to dig deeper. A node in this state might be experiencing network issues, resource exhaustion, or kubelet problems.</p>
<h2 id="check-resource-usage-at-a-glance">Check Resource Usage at a Glance</h2>
<p>Next, get a quick overview of resource consumption across your nodes to identify potential bottlenecks.</p>
<pre><code class="hljs">kubectl top nodes
</code></pre>
<p>This command shows CPU and memory usage for each node in your cluster. It provides both absolute values and percentages, making it easy to spot resource pressure.</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>Keep an eye out for:</p>
<ul>
<li>CPU or memory regularly above 80% on any node</li>
<li>One node doing all the heavy lifting while others are barely working</li>
<li>Sudden spikes that don't match your expected workload patterns</li>
</ul>
<p>No <code>metrics-server</code> running? Install it with this command:</p>
<pre><code class="hljsgo">kubectl apply -f https:<span class="hljs-comment">//github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml</span>
</code></pre>
<p>The metrics-server is essential for resource monitoring and is required for horizontal pod autoscaling to work properly.</p>
<h2 id="look-at-all-pods-across-all-namespaces">Look at All Pods Across All Namespaces</h2>
<p>Get a bird's-eye view of all pods running in your cluster to quickly identify any that are misbehaving.</p>
<pre><code class="hljsjavascript">kubectl <span class="hljs-keyword">get</span> pods --all-namespaces
</code></pre>
<p>This command lists every pod across all namespaces, showing their current status, restart count, and age. It's like taking the pulse of your entire application ecosystem.</p>
<p>Healthy pods should be <code>Running</code> or <code>Completed</code>. If you see states like <code>CrashLoopBackOff</code>, <code>ImagePullBackOff</code>, <code>Pending</code>, or <code>Error</code>, note the namespace and pod name for further investigation.</p>
<p>Also watch the <strong>RESTARTS</strong> column closely. If a pod has restarted a dozen times in the last hour, something's definitely off. Frequent restarts often indicate:</p>
<ul>
<li>Application crashes due to bugs or configuration issues</li>
<li>Failing health checks (readiness or liveness probes)</li>
<li>Resource limits being exceeded</li>
<li>Dependencies being unavailable</li>
</ul>
<h2 id="zoom-in-on-problem-pods">Zoom In on Problem Pods</h2>
<p>When you spot problematic pods, dig deeper to understand what's causing the issues.</p>
<pre><code class="hljsxml">kubectl describe pod <span class="hljs-tag">&lt;<span class="hljs-name">pod-name</span>&gt;</span> -n <span class="hljs-tag">&lt;<span class="hljs-name">namespace</span>&gt;</span>
</code></pre>
<p>Replace <code>&lt;pod-name&gt;</code> and <code>&lt;namespace&gt;</code> with the actual values from your problem pods. This command provides detailed information about the pod's configuration, current state, and recent events.</p>
<p>Check for these common issues:</p>
<ul>
<li><strong>Events at the bottom</strong> (often the smoking gun that reveals the root cause)</li>
<li><strong>Failing readiness or liveness probes</strong> that prevent the pod from receiving traffic</li>
<li><strong>Image pull errors</strong> indicating registry access problems or incorrect image names</li>
<li><strong>Resource limit issues</strong> where the pod exceeds its memory or CPU constraints</li>
</ul>
<p>The events section is particularly valuable because it shows a chronological history of what happened to the pod, including scheduling decisions, volume mounts, and error conditions.</p>
<h2 id="check-the-cluster-s-event-log">Check the Cluster's Event Log</h2>
<p>Get insight into what's been happening across your entire cluster by examining recent events.</p>
<pre><code class="hljsjavascript">kubectl <span class="hljs-keyword">get</span> events --sort-by=.metadata.creationTimestamp
</code></pre>
<p>This command shows cluster-wide events sorted by when they occurred, giving you a timeline of recent activity. Events provide context about system-level operations and can reveal patterns or issues that affect multiple components.</p>
<p>Events will tell you what's been happening behind the scenes:</p>
<ul>
<li>Failed volume mounts that prevent pods from starting</li>
<li>DNS resolution errors affecting service communication</li>
<li>Scheduling issues when pods can't be placed on nodes</li>
<li>Node pressure warnings indicating resource constraints</li>
</ul>
<h2 id="try-k9s-for-a-better-view">Try k9s for a Better View</h2>
<p>If you want something more interactive than command-line tools, give <strong><a href="https://k9scli.io/">k9s</a></strong> a try. It's a terminal-based UI for Kubernetes that provides real-time cluster information in an intuitive interface.</p>
<p>k9s lets you browse resources, view logs, and drill into problems without typing long commands. You can navigate between different resource types using simple keystrokes, filter resources, and even perform actions like scaling deployments or deleting pods.</p>
<p>Once you try k9s, it's hard to go back to plain kubectl for exploratory tasks. It's particularly useful when you need to quickly jump between different namespaces or resource types during troubleshooting.</p>
<p>Five minutes a day is all it takes to stay ahead of most cluster problems. Make this health check part of your daily routine and you'll catch issues before they blow up and before your pager goes off at 3 a.m. Regular monitoring helps you understand your cluster's normal behavior, making it easier to spot anomalies when they occur.</p>
]]></description>
                                                            <category>docker</category>
                                            <category>linux</category>
                                            <category>cloud</category>
                                            <category>devops</category>
                                            <category>kubernetes</category>
                                                    <author><![CDATA[DevOps Daily]]></author>
                <guid>https://devdojo.com/13519</guid>
                <pubDate>Fri, 15 Aug 2025 03:35:21 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Livewire 4: The Future of PHP Components]]></title>
                <link>https://devdojo.com/tnylea/livewire-4-the-future-of-php-components</link>
                <description><![CDATA[<p>This week at Laracon <a href="https://www.youtube.com/live/GM0glP77tsA?si=7z066MlnYCXa3uC4" target="_blank">Caleb Porzio announced Livewire 4</a>, which comes with unified components, performance improvements, and many more awesome goodies 🤤</p>
<h2 id="unifying-livewire">Unifying Livewire</h2>
<p>Caleb takes the stage to address one of the most pressing issues in the Livewire's ecosystem: fragmentation. With three different ways to create Livewire components (traditional, Volt functional, and Volt class-based), the community had become divided, and newcomers were left confused about the "right" way to build components.</p>
<p>"The whole community, it's forked," Porzio admitted. "There's three different ways to make a livewire component... This is not good for newcomers, it's not good for old-timers, and everybody's guessing what's the best way."</p>
<p>His solution? A fourth way that unifies everything.</p>
<h2 id="the-new-default-single-file-components">The New Default: Single-File Components</h2>
<p>In Livewire 4, running <code>php artisan make:livewire counter</code> creates a single-file, class-based component by default. This isn't Volt anymore—it's just Livewire, powered by a custom parser.</p>
<pre><code class="hljsxml"><span class="php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">use</span> <span class="hljs-title">Livewire</span>\<span class="hljs-title">Component</span>;

<span class="hljs-keyword">new</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Component</span> </span>{
    <span class="hljs-keyword">public</span> $count = <span class="hljs-number">1</span>;
    
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">increment</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;count++;
    }
    
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">decrement</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;count--;
    }
}; <span class="hljs-meta">?&gt;</span></span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{ $count }}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">wire:click</span>=<span class="hljs-string">"increment"</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">wire:click</span>=<span class="hljs-string">"decrement"</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">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">this</span>.watch(<span class="hljs-string">'count'</span>, (value) =&gt; {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Count changed to'</span>, value);
});
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h3 id="what-s-new-in-single-file-components">What's New in Single-File Components</h3>
<p><strong>Native JavaScript Integration</strong>: No more <code>@script</code> directive needed. Just add a <code>&lt;script&gt;</code> tag at the bottom of your file, and use <code>this.</code> instead of <code>$wire</code>:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">this</span>.watch(<span class="hljs-string">'count'</span>, (value) =&gt; {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Count changed to'</span>, value);
});
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p><strong>Interceptors</strong>: Hook into any part of the request lifecycle:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">this</span>.intercept(<span class="hljs-string">'increment'</span>, ({ proceed, cancel }) =&gt; {
    <span class="hljs-keyword">if</span> (confirm(<span class="hljs-string">'Are you sure?'</span>)) {
        proceed();
    } <span class="hljs-keyword">else</span> {
        cancel();
    }
});
</code></pre>
<h2 id="multi-file-components-the-best-of-both-worlds">Multi-File Components: The Best of Both Worlds</h2>
<p>Not everyone loves single-file components, and Porzio gets it. When you run <code>make:livewire</code> on an existing component, you get this option:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">php</span> <span class="hljs-selector-tag">artisan</span> <span class="hljs-selector-tag">make</span><span class="hljs-selector-pseudo">:livewire</span> <span class="hljs-selector-tag">counter</span> <span class="hljs-selector-tag">--mfc</span>
</code></pre>
<p>This creates a multi-file component (MFC) structure:</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">⚡ counter/
├── counter.php
├── counter.blade.php
└── counter.js
</code></pre>
<p>The PHP file is pure PHP (an anonymous class), the Blade file is pure Blade, and the JavaScript file is served as an ES6 module with automatic code-splitting and caching.</p>
<h2 id="new-project-structure-opinions">New Project Structure Opinions</h2>
<p>Livewire 4 introduces opinionated folder structures to reduce decision fatigue:</p>
<ul>
<li><strong><code>resources/views/components/</code></strong> - All your components (Blade and Livewire)</li>
<li><strong><code>resources/views/pages/</code></strong> - Page-level components</li>
<li><strong><code>resources/views/layouts/</code></strong> - Layout components</li>
</ul>
<p>These directories are automatically namespaced, making component organization cleaner and more predictable.</p>
<h2 id="file-naming-the-lightning-bolt-controversy">File Naming: The Lightning Bolt Controversy</h2>
<p>Here's where things get interesting (and controversial). Porzio introduced the lightning bolt emoji (⚡) as a file prefix for Livewire components:</p>
<pre><code class="hljscss">⚡<span class="hljs-selector-tag">counter</span><span class="hljs-selector-class">.livewire</span><span class="hljs-selector-class">.php</span>
</code></pre>
<p>While admitting he might be the only one who likes it, Porzio argued that Unicode emojis work across all file systems and provide instant visual recognition. The precedent exists in other frameworks (SvelteKit, Next.js) and even programming languages like Mojo.</p>
<p>"When did we stop having fun?" Porzio asked. "We're all gonna be out of jobs anyway... Let's have fun on our way out with emojis."</p>
<h2 id="leveraging-php-8-4-s-property-hooks">Leveraging PHP 8.4's Property Hooks</h2>
<p>One of the most exciting features in Livewire 4 is deep integration with PHP 8.4's new property hooks:</p>
<h3 id="validation-with-setters">Validation with Setters</h3>
<pre><code class="hljsphp"><span class="hljs-keyword">public</span> int $count {
    set =&gt; max(<span class="hljs-number">1</span>, $value);
}
</code></pre>
<p>This automatically prevents the count from going below 1, eliminating the need for updating hooks in many cases.</p>
<h3 id="computed-properties-with-getters">Computed Properties with Getters</h3>
<pre><code class="hljsphp"><span class="hljs-keyword">public</span> int $multiple {
    get =&gt; <span class="hljs-keyword">$this</span>-&gt;count * <span class="hljs-number">5</span>;
}
</code></pre>
<p>Access it in your template like any other property:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Count: {{ $count }}, Times 5: {{ $multiple }}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h3 id="advanced-caching-with-property-hooks">Advanced Caching with Property Hooks</h3>
<p>You can even implement caching directly in property hooks:</p>
<pre><code class="hljsphp"><span class="hljs-keyword">public</span> string $expensiveData {
    get =&gt; cache()-&gt;remember(<span class="hljs-string">"data-{$this-&gt;id}"</span>, <span class="hljs-number">3600</span>, fn() =&gt; <span class="hljs-keyword">$this</span>-&gt;fetchExpensiveData());
    set =&gt; cache()-&gt;put(<span class="hljs-string">"data-{$this-&gt;id}"</span>, $value, <span class="hljs-number">3600</span>);
}
</code></pre>
<h3 id="asymmetric-visibility">Asymmetric Visibility</h3>
<p>PHP 8.4 also supports asymmetric property visibility:</p>
<pre><code class="hljsphp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">private</span>(set) string $readOnlyProperty;
</code></pre>
<p>This replaces Livewire's <code>#[Locked]</code> attribute with native PHP functionality.</p>
<h2 id="enhanced-loading-states">Enhanced Loading States</h2>
<p>Livewire 4 automatically adds <code>data-loading</code> attributes to any element that triggers a network request:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">wire:click</span>=<span class="hljs-string">"save"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"data-[loading]:opacity-50"</span>&gt;</span>
    Save
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"data-[loading]:block hidden"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Loading...<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>This integrates beautifully with Tailwind CSS 4's data attribute selectors.</p>
<h2 id="the-long-awaited-slots-feature">The Long-Awaited Slots Feature</h2>
<p>After years of requests, Livewire 4 finally supports slots:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">livewire:modal</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">wire:submit</span>=<span class="hljs-string">"save"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">wire:model</span>=<span class="hljs-string">"title"</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>&gt;</span>Save<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">livewire:modal</span>&gt;</span>
</code></pre>
<p>The modal component works exactly like Blade components:</p>
<pre><code class="hljsxml"><span class="hljs-comment">&lt;!-- modal.blade.php --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"modal"</span>&gt;</span>
    {{ $slot }}
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h3 id="component-refs-for-communication">Component Refs for Communication</h3>
<p>Along with slots comes a new <code>wire:ref</code> system for parent-child communication:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">livewire:modal</span> <span class="hljs-attr">wire:ref</span>=<span class="hljs-string">"modal"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">wire:submit</span>=<span class="hljs-string">"save"</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- form content --&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">livewire:modal</span>&gt;</span>
</code></pre>
<pre><code class="hljsphp"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">save</span><span class="hljs-params">()</span>
</span>{
    <span class="hljs-comment">// Save logic...</span>
    <span class="hljs-keyword">$this</span>-&gt;dispatch(<span class="hljs-string">'close'</span>)-&gt;to(ref: <span class="hljs-string">'modal'</span>);
}
</code></pre>
<p>This eliminates the need for complex event broadcasting patterns when you need direct parent-child communication.</p>
<hr>
<p>Ok, so far we covered the new rendering engine and pulling capabilities. Next, let's dive into the performance optimizations.</p>
<hr>
<h2 id="the-performance-problem-when-components-become-bottlenecks">The Performance Problem: When Components Become Bottlenecks</h2>
<p>Before diving into solutions, Caleb demonstrated a critical performance issue that many Laravel developers face but rarely measure. Using a simple benchmark, he showed how Blade components can become surprising bottlenecks:</p>
<pre><code class="hljsphp"><span class="hljs-comment">// Simple benchmark: 25,000 Blade components in a loop</span>
$start = microtime(<span class="hljs-keyword">true</span>);
<span class="hljs-keyword">for</span> ($i = <span class="hljs-number">0</span>; $i &lt; <span class="hljs-number">25000</span>; $i++) {
    <span class="hljs-comment">// Render a simple Blade component</span>
}
$end = microtime(<span class="hljs-keyword">true</span>);
<span class="hljs-keyword">echo</span> ($end - $start) * <span class="hljs-number">1000</span> . <span class="hljs-string">" milliseconds"</span>;
</code></pre>
<p><strong>The shocking results:</strong></p>
<ul>
<li>First run (compilation): <strong>508 milliseconds</strong></li>
<li>Second run (cached): <strong>274 milliseconds</strong></li>
<li>Pure PHP require: <strong>26 milliseconds</strong></li>
<li>Plain div + echo: <strong>21 milliseconds</strong></li>
</ul>
<p>This revealed that even cached Blade components carry significant overhead - about 10x slower than plain PHP.</p>
<hr>
<h2 id="blaze-the-blade-compiler-revolution">Blaze - The Blade Compiler Revolution</h2>
<h3 id="what-is-blaze">What is Blaze?</h3>
<p>Blaze is a revolutionary Blade optimization layer that uses <strong>code folding</strong> to eliminate runtime overhead. Instead of aggressive caching (which creates cache management problems), Blaze analyzes your Blade templates at compile time and pre-renders static portions.</p>
<pre><code class="hljsxml">// Before Blaze - Complex compiled output with framework overhead
<span class="php"><span class="hljs-meta">&lt;?php</span> $__env-&gt;startComponent(<span class="hljs-string">'components.card'</span>); <span class="hljs-meta">?&gt;</span></span>
    <span class="php"><span class="hljs-meta">&lt;?php</span> $__env-&gt;slot(<span class="hljs-string">'title'</span>); <span class="hljs-meta">?&gt;</span></span>Product Name<span class="php"><span class="hljs-meta">&lt;?php</span> $__env-&gt;endSlot(); <span class="hljs-meta">?&gt;</span></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="php"><span class="hljs-meta">&lt;?php</span> <span class="hljs-keyword">echo</span> e($product-&gt;description); <span class="hljs-meta">?&gt;</span></span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="php"><span class="hljs-meta">&lt;?php</span> <span class="hljs-keyword">echo</span> $__env-&gt;renderComponent(); <span class="hljs-meta">?&gt;</span></span>

// After Blaze - Clean, optimized output
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-title"</span>&gt;</span>Product Name<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">"content"</span>&gt;</span><span class="php"><span class="hljs-meta">&lt;?php</span> <span class="hljs-keyword">echo</span> e($product-&gt;description); <span class="hljs-meta">?&gt;</span></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>
<h3 id="how-code-folding-works">How Code Folding Works</h3>
<p>Code folding identifies parts of your template that never change in production:</p>
<pre><code class="hljsxml">{{-- This Blade component --}}
<span class="hljs-tag">&lt;<span class="hljs-name">x-card</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-white shadow-lg"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">x-slot:title</span>&gt;</span>{{ $title }}<span class="hljs-tag">&lt;/<span class="hljs-name">x-slot:title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-4"</span>&gt;</span>
        {{ $content }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">x-card</span>&gt;</span>
</code></pre>
<p>Blaze recognizes that the card structure, CSS classes, and HTML elements are static. Only the <code>$title</code> and <code>$content</code> variables are dynamic. It pre-renders the static parts at compile time.</p>
<h3 id="installation-and-usage">Installation and Usage</h3>
<pre><code class="hljsjavascript">composer <span class="hljs-built_in">require</span> livewire/blaze
</code></pre>
<p>That's it! Blaze works transparently with your existing Blade templates. You can optionally opt-in with configuration, but the goal is zero-configuration optimization.</p>
<h3 id="performance-impact">Performance Impact</h3>
<p><strong>Real-world results from the demo:</strong></p>
<ul>
<li><strong>Before Blaze</strong>: 29,000 views rendered in 1.6 seconds</li>
<li><strong>After Blaze</strong>: 100 views rendered in 131 milliseconds</li>
</ul>
<p>That's more than a <strong>10x performance improvement</strong> while maintaining the exact same developer experience.</p>
<hr>
<h2 id="islands-architecture">Islands Architecture</h2>
<h3 id="the-problem-with-monolithic-components">The Problem with Monolithic Components</h3>
<p>Traditional Livewire components re-render entirely on any update. Consider this dashboard:</p>
<pre><code class="hljsphp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Dashboard</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Component</span> 
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">render</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-keyword">return</span> view(<span class="hljs-string">'dashboard'</span>, [
            <span class="hljs-string">'analytics'</span> =&gt; <span class="hljs-keyword">$this</span>-&gt;getAnalytics(), <span class="hljs-comment">// Fast query</span>
            <span class="hljs-string">'revenue'</span> =&gt; <span class="hljs-keyword">$this</span>-&gt;getAccountRevenue(), <span class="hljs-comment">// SLOW - 1 second query</span>
            <span class="hljs-string">'reports'</span> =&gt; <span class="hljs-keyword">$this</span>-&gt;getReports(), <span class="hljs-comment">// Fast query</span>
        ]);
    }
    
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generateReport</span><span class="hljs-params">()</span> 
    </span>{
        <span class="hljs-comment">// This action forces re-render of EVERYTHING</span>
        <span class="hljs-comment">// Including the slow revenue calculation</span>
    }
}
</code></pre>
<p>Every action triggers the expensive <code>getAccountRevenue()</code> query, making the entire interface sluggish.</p>
<h3 id="islands-surgical-component-isolation">Islands: Surgical Component Isolation</h3>
<p>Islands allow you to isolate expensive parts of your template:</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">"dashboard"</span>&gt;</span>
    {{-- Fast section - always responsive --}}
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"analytics"</span>&gt;</span>
        @foreach($analytics as $metric)
            <span class="hljs-tag">&lt;<span class="hljs-name">x-metric</span> <span class="hljs-attr">:value</span>=<span class="hljs-string">"$metric-&gt;value"</span> <span class="hljs-attr">:label</span>=<span class="hljs-string">"$metric-&gt;name"</span> /&gt;</span>
        @endforeach
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    
    {{-- Slow section - isolated on an island --}}
    @island
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"revenue-chart"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">x-chart</span> <span class="hljs-attr">:data</span>=<span class="hljs-string">"$this-&gt;accountRevenue"</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    @endisland
    
    {{-- Fast section - always responsive --}}
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"reports"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">wire:click</span>=<span class="hljs-string">"generateReport"</span>&gt;</span>Generate Report<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">wire:click</span>=<span class="hljs-string">"downloadReport"</span>&gt;</span>Download<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>
</code></pre>
<h3 id="island-benefits">Island Benefits</h3>
<ol>
<li><strong>Isolation</strong>: Actions outside the island don't trigger island re-renders</li>
<li><strong>Performance</strong>: Only the island content is processed for island-specific updates</li>
<li><strong>Responsiveness</strong>: The rest of your interface stays snappy</li>
</ol>
<p><strong>Before Islands:</strong></p>
<ul>
<li>Generate Report: 1+ second (re-renders everything including slow revenue)</li>
<li>Download: 1+ second (same problem)</li>
</ul>
<p><strong>After Islands:</strong></p>
<ul>
<li>Generate Report: Instant (skips the island)</li>
<li>Download: Instant (skips the island)</li>
</ul>
<h3 id="lazy-loading-with-islands">Lazy Loading with Islands</h3>
<p>Islands support lazy loading with elegant placeholder handling:</p>
<pre><code class="hljsperl">@island(lazy: true)
    @placeholder
        &lt;<span class="hljs-keyword">x</span>-revenue-placeholder /&gt;
    @endplaceholder
    
    &lt;div class=<span class="hljs-string">"revenue-section"</span>&gt;
        &lt;<span class="hljs-keyword">x</span>-chart :data=<span class="hljs-string">"$this-&gt;expensiveRevenueData"</span> /&gt;
    &lt;<span class="hljs-regexp">/div&gt;
@endisland
</span></code></pre>
<p><strong>User Experience:</strong></p>
<ol>
<li>Page loads instantly with placeholder</li>
<li>Revenue section loads asynchronously</li>
<li>Smooth transition from skeleton to real data</li>
</ol>
<h3 id="advanced-island-features">Advanced Island Features</h3>
<h4 id="named-islands-with-remote-targeting">Named Islands with Remote Targeting</h4>
<pre><code class="hljspowershell">@island(<span class="hljs-string">'reports'</span>)
    @<span class="hljs-keyword">foreach</span>(<span class="hljs-variable">$reports</span> as <span class="hljs-variable">$report</span>)
        &lt;div <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">report</span>-<span class="hljs-title">item</span>"&gt;</span>{{ <span class="hljs-variable">$report</span>-&gt;title }}&lt;/div&gt;
    @endforeach
@endisland

{{-- This button is outside the island but targets it --}}
&lt;button wire:island=<span class="hljs-string">"reports"</span> wire:click=<span class="hljs-string">"loadMoreReports"</span>&gt;
    Load More
&lt;/button&gt;
</code></pre>
<h4 id="flexible-rendering-modes">Flexible Rendering Modes</h4>
<pre><code class="hljsxml">{{-- Default: Replace island content --}}
@island('reports')
    <span class="hljs-comment">&lt;!-- content --&gt;</span>
@endisland

{{-- Append mode: Add new content to existing --}}
<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">wire:island</span>=<span class="hljs-string">"reports"</span> <span class="hljs-attr">wire:click</span>=<span class="hljs-string">"loadMore"</span> <span class="hljs-attr">wire:render</span>=<span class="hljs-string">"append"</span>&gt;</span>
    Load More
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

{{-- Prepend mode: Add content to the beginning --}}
<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">wire:island</span>=<span class="hljs-string">"chat"</span> <span class="hljs-attr">wire:click</span>=<span class="hljs-string">"loadOlderMessages"</span> <span class="hljs-attr">wire:render</span>=<span class="hljs-string">"prepend"</span>&gt;</span>
    Load Older Messages
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<h4 id="infinite-scroll-with-intersection-observer">Infinite Scroll with Intersection Observer</h4>
<p>The ultimate modern UX pattern:</p>
<pre><code class="hljspowershell">@island(<span class="hljs-string">'posts'</span>, render: <span class="hljs-string">'append'</span>)
    @<span class="hljs-keyword">foreach</span>(<span class="hljs-variable">$posts</span> as <span class="hljs-variable">$post</span>)
        &lt;article <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">post</span>"&gt;</span>{{ <span class="hljs-variable">$post</span>-&gt;content }}&lt;/article&gt;
    @endforeach
@endisland

{{-- Invisible trigger element --}}
&lt;div wire:island=<span class="hljs-string">"posts"</span> 
     wire:intersect=<span class="hljs-string">"<span class="hljs-variable">$paginator</span>.nextPage()"</span>
     <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">h</span>-4"&gt;
&lt;/<span class="hljs-title">div</span>&gt;
</span></code></pre>
<p>This creates true infinite scroll:</p>
<ul>
<li>✅ Only fetches new data when needed</li>
<li>✅ Only renders new content</li>
<li>✅ Preserves scroll position</li>
<li>✅ Minimal DOM manipulation</li>
<li>✅ Zero JavaScript required</li>
</ul>
<h3 id="islands-with-polling">Islands with Polling</h3>
<p>Real-time updates for specific sections:</p>
<pre><code class="hljsxml">@island(poll: '5s')
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"live-metrics"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Active Users: {{ $activeUsers }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Revenue Today: ${{ number_format($todayRevenue) }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
@endisland
</code></pre>
<p>Only the island polls - the rest of your page remains untouched.</p>
<hr>
<h2 id="real-world-performance-results">Real-World Performance Results</h2>
<h3 id="the-complete-solution">The Complete Solution</h3>
<p>Combining all three optimizations (new rendering engine + Blaze + Islands):</p>
<pre><code class="hljsperl">{{-- A complex dashboard that loads instantly <span class="hljs-keyword">and</span> stays responsive --}}
&lt;div class=<span class="hljs-string">"dashboard"</span>&gt;
    {{-- Fast sections with Blaze optimization --}}
    &lt;<span class="hljs-keyword">x</span>-metric-grid :metrics=<span class="hljs-string">"$quickMetrics"</span> /&gt;
    
    {{-- Expensive section isolated <span class="hljs-keyword">and</span> lazy-loaded --}}
    @island(<span class="hljs-string">'revenue'</span>, lazy: true)
        @placeholder
            &lt;<span class="hljs-keyword">x</span>-revenue-skeleton /&gt;
        @endplaceholder
        
        &lt;<span class="hljs-keyword">x</span>-revenue-chart :data=<span class="hljs-string">"$expensiveRevenueData"</span> /&gt;
    @endisland
    
    {{-- Interactive section with infinite scroll --}}
    @island(<span class="hljs-string">'reports'</span>, render: <span class="hljs-string">'append'</span>)
        @foreach($reports as $report)
            &lt;<span class="hljs-keyword">x</span>-report-card :report=<span class="hljs-string">"$report"</span> /&gt;
        @endforeach
    @endisland
    
    &lt;div wire:island=<span class="hljs-string">"reports"</span> 
         wire:intersect=<span class="hljs-string">"$paginator.nextPage()"</span>&gt;
    &lt;<span class="hljs-regexp">/div&gt;
&lt;/div</span>&gt;
</code></pre>
<p><strong>Performance characteristics:</strong></p>
<ul>
<li><strong>Initial load</strong>: Instant (lazy islands + Blaze optimization)</li>
<li><strong>User interactions</strong>: Always responsive (isolated islands)</li>
<li><strong>Real-time updates</strong>: Surgical precision (targeted polling)</li>
<li><strong>Infinite scroll</strong>: Smooth and performant (append rendering)</li>
</ul>
<hr>
<h2 id="developer-experience-simple-yet-powerful">Developer Experience: Simple Yet Powerful</h2>
<h3 id="the-philosophy">The Philosophy</h3>
<p>Livewire v4's performance features follow a key principle: <strong>maximum performance with minimal complexity</strong>.</p>
<pre><code class="hljsperl">{{-- This is all you need <span class="hljs-keyword">for</span> infinite scroll --}}
@island(<span class="hljs-string">'posts'</span>, render: <span class="hljs-string">'append'</span>)
    @foreach($posts as $post)
        &lt;<span class="hljs-keyword">x</span>-post :post=<span class="hljs-string">"$post"</span> /&gt;
    @endforeach
@endisland

&lt;div wire:island=<span class="hljs-string">"posts"</span> wire:intersect=<span class="hljs-string">"$paginator.nextPage()"</span>&gt;&lt;<span class="hljs-regexp">/div&gt;
</span></code></pre>
<p>Compare this to traditional JavaScript approaches requiring:</p>
<ul>
<li>Complex state management</li>
<li>Manual DOM manipulation</li>
<li>Scroll position tracking</li>
<li>Loading state handling</li>
<li>Error boundary management</li>
</ul>
<h3 id="learning-curve">Learning Curve</h3>
<p>The beauty of islands is in their composability. Learn the basic primitive:</p>
<pre><code class="hljsxml">@island
    <span class="hljs-comment">&lt;!-- expensive content --&gt;</span>
@endisland
</code></pre>
<p>Then combine with modifiers:</p>
<ul>
<li><code>lazy: true</code> - Lazy loading</li>
<li><code>poll: '5s'</code> - Real-time updates</li>
<li><code>render: 'append'</code> - Additive rendering</li>
<li><code>wire:intersect</code> - Intersection triggers</li>
</ul>
<hr>
<h2 id="conclusion-a-new-era-for-laravel-frontend">Conclusion: A New Era for Laravel Frontend</h2>
<p>Livewire v4 represents a massive leap forward for Laravel applications. For Laravel developers, this means you can build complex, interactive applications without sacrificing performance or developer experience. The era of choosing between "fast" and "feature-rich" is over.</p>
]]></description>
                                                                    <author><![CDATA[Tony Lea]]></author>
                <guid>https://devdojo.com/13497</guid>
                <pubDate>Thu, 31 Jul 2025 14:24:57 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Laracon 2025 Keynote]]></title>
                <link>https://devdojo.com/tnylea/laracon-2025-keynote</link>
                <description><![CDATA[<p>This year's Laracon keynote was absolutely insane! There were seven segments packed with open source goodies, AI-powered development tools, and infrastructure that'll make you rethink what modern web development can be. Here's everything that went down.</p>
<ol>
<li><a href="#new-framework-features">🎯 New Framework Features</a> (Taylor Otwell)</li>
<li><a href="#open-source-deep-dive">🚀 Open Source Deep Dive</a> (Joe Tannenbaum)</li>
<li><a href="#laravel-wayfinder-type-safety-nirvana">🤖 Laravel AI</a> (Ashley Hindle)</li>
<li><a href="#laravel-design">🎨 Laravel Design</a> (David Hill)</li>
<li><a href="#nightwatch-updates">🌙 Nightwatch</a> (Taylor Otwell)</li>
<li><a href="#forge-2-0">⚒ Forge 2.0</a> (James Brooks)</li>
<li><a href="#laravel-cloud">☁️ Laravel Cloud</a> (Joe Dixon)</li>
</ol>
<p><img src="https://cdn.devdojo.com/images/july2025/speakers.jpg" alt="speakers"></p>
<p>Taylor kicked things off with welcoming everyone to Laracon and expressing how this eco-system has never felt stronger. After he spits some good vibes, he starts things off by talking about some new framework features.</p>
<h2 id="new-framework-features">🎯 New Framework Features</h2>
<p><strong>Attribute-Based Container Bindings</strong></p>
<p><img src="https://cdn.devdojo.com/images/july2025/attr-binding.png" alt="attr-binding"></p>
<p>Remember trudging over to your <code>AppServiceProvider</code> to bind interfaces? Those days are numbered.</p>
<p><strong>The Old Way:</strong></p>
<pre><code class="hljsphp"><span class="hljs-keyword">$this</span>-&gt;app-&gt;bind(PaymentProcessor::class, Stripe::class);
</code></pre>
<p><strong>The New Way:</strong></p>
<pre><code class="hljsphp"><span class="hljs-comment">#[Bind(Stripe::class)]</span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">PaymentProcessor</span>
</span>{
    <span class="hljs-comment">// That's it. You're done.</span>
}
</code></pre>
<p><img src="https://cdn.devdojo.com/images/july2025/screenshot_09_30_cropped.png" alt="screenshot_09_30_cropped"></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>But wait, there's more! Environment-specific bindings are here:</p>
<pre><code class="hljsphp"><span class="hljs-comment">#[Bind(Stripe::class)]</span>
<span class="hljs-comment">#[Bind(FakeStripe::class, environments: ['local'])]</span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">PaymentProcessor</span>
</span>{
    <span class="hljs-comment">// Production gets real Stripe, local gets fake version</span>
}
</code></pre>
<p>The attribute also supports <code>#[Singleton]</code> and <code>#[Scoped]</code> decorators for fine-grained instance management—especially useful for queued jobs and Octane applications.</p>
<p><strong>Fluent URI Builder: URL Manipulation That Doesn't Suck</strong></p>
<p>If you've ever tried to manually manipulate URL strings and wanted to throw your computer out the window, this one's for you.</p>
<p>Instead of string concatenation nightmares:</p>
<pre><code class="hljsphp">$uri = URI::for(<span class="hljs-string">'https://laravel.com'</span>)
    -&gt;withPath(<span class="hljs-string">'/docs'</span>)
    -&gt;withQuery([<span class="hljs-string">'version'</span> =&gt; <span class="hljs-string">'11.x'</span>, <span class="hljs-string">'page'</span> =&gt; <span class="hljs-string">'routing'</span>])
    -&gt;withoutQuery(<span class="hljs-string">'page'</span>)  <span class="hljs-comment">// Remove a query param</span>
    -&gt;withFragment(<span class="hljs-string">'installation'</span>);
</code></pre>
<p>The real elegance? You can return these URI objects directly from routes:</p>
<pre><code class="hljsphp">Route::get(<span class="hljs-string">'/redirect'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">return</span> URI::route(<span class="hljs-string">'users.index'</span>)
        -&gt;withQuery([<span class="hljs-string">'filter'</span> =&gt; <span class="hljs-string">'active'</span>]);
});
</code></pre>
<p>Laravel automatically generates redirects. The readability is <em>chef's kiss</em> perfect.</p>
<p><strong>Cache Memoization: Fix Performance Without Thinking</strong></p>
<p>Picture this: you're calling <code>Cache::get('expensive-key')</code> multiple times throughout a single request. Each call hits your cache store, even though you're asking for the same thing.</p>
<p>In Taylor's demo, a route that called the cache four times resulted in four actual cache queries. Not ideal.</p>
<p><strong>The Solution:</strong></p>
<pre><code class="hljsphp"><span class="hljs-comment">// Instead of this</span>
$value = Cache::get(<span class="hljs-string">'expensive-key'</span>);

<span class="hljs-comment">// Do this</span>
$value = Cache::memoGet(<span class="hljs-string">'expensive-key'</span>);
</code></pre>
<p>Laravel keeps the value in memory after the first retrieval. Subsequent calls return instantly without hitting the cache store.</p>
<p>The clever bit? If you <code>Cache::put()</code> a new value during the request, Laravel automatically updates the memoized version. No stale data, no manual cache busting.</p>
<hr>
<h2 id="open-source-deep-dive">🚀 Open Source Deep Dive</h2>
<p>Joe took the stage to deliver some serious open source improvements that'll make your daily development life significantly better.</p>
<p><strong>Broadcasting's JavaScript Makeover</strong></p>
<p><img src="https://cdn.devdojo.com/images/july2025/screenshot_22_00_cropped.png" alt="screenshot_22_00_cropped"></p>
<p>The old <code>php artisan install:broadcasting</code> command got a complete overhaul. Instead of choosing a provider and figuring out the rest yourself, Laravel now asks for all required information upfront:</p>
<ul>
<li>Automatically writes correct ENV keys to your <code>.env</code> file</li>
<li>Provides Vite configuration keys for JavaScript consumption</li>
<li>Detects your frontend framework (Vue/React) and configures accordingly</li>
<li>Installs appropriate Laravel Echo packages automatically</li>
</ul>
<p><strong>Goodbye Boilerplate, Hello Hooks</strong></p>
<p>Remember this nightmare?</p>
<pre><code class="hljsphp"><span class="hljs-comment">// Old way - lifecycle management hell</span>
onMounted(() =&gt; {
  channel = <span class="hljs-keyword">Echo</span>.channel(<span class="hljs-string">'orders'</span>)
    .listen(<span class="hljs-string">'OrderShipped'</span>, (e) =&gt; {
      <span class="hljs-comment">// handle event</span>
    });
});

onUnmounted(() =&gt; {
  <span class="hljs-keyword">Echo</span>.leave(<span class="hljs-string">'orders'</span>);
});
</code></pre>
<p>Now it's just this:</p>
<pre><code class="hljsgo"><span class="hljs-comment">// New way - clean hooks</span>
<span class="hljs-keyword">const</span> { data } = useEcho({
  channel: <span class="hljs-string">'orders'</span>,
  event: <span class="hljs-string">'OrderShipped'</span>
});
</code></pre>
<p>The hooks automatically handle mounting/unmounting, provide type safety, and come in flavors for public, private, presence, and model channels. Available for both Vue and React!</p>
<p><strong>Streaming Made Simple</strong></p>
<p>While Laravel's server-side streaming is already fantastic (10 lines of code!), consuming these streams on the JavaScript side was getting messy.</p>
<p><img src="https://cdn.devdojo.com/images/july2025/screenshot_26_00_cropped.png" alt="screenshot_26_00_cropped"></p>
<p>Enter stream hooks:</p>
<pre><code class="hljsgo"><span class="hljs-keyword">const</span> { data, isStreaming, cancel, restart } = useStream(<span class="hljs-string">'/api/chat'</span>);
</code></pre>
<p>These hooks automatically concatenate streaming data, provide connection state, handle cancellation, and parse JSON. All the complexity hidden behind one powerful hook.</p>
<p><strong>Inertia Forms Revolution</strong></p>
<p>Traditional Inertia forms require a lot of boilerplate with <code>useForm()</code>.</p>
<p>The new Inertia Form component is a drop-in replacement for the HTML form tag:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">Form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"/login"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"post"</span> <span class="hljs-attr">v-slot</span>=<span class="hljs-string">"{ data, errors, processing }"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"password"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">:disabled</span>=<span class="hljs-string">"processing"</span>&gt;</span>Login<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>
</code></pre>
<p>No more <code>v-model</code> bindings, no more <code>form.something</code> references everywhere. Just use <code>name</code> attributes and get form state through slot props. Launching in the next couple weeks.</p>
<hr>
<p><strong>Laravel Wayfinder: Type Safety Nirvana</strong></p>
<p><img src="https://cdn.devdojo.com/images/july2025/screenshot_28_30_cropped.png" alt="screenshot_28_30_cropped"></p>
<p>Joe addressed the elephant in the room—the gap between PHP and TypeScript. The client needs to know what the server has defined, but it's often just educated guesses with magic strings.</p>
<p><strong>Magic Strings Are Dead</strong></p>
<p>Instead of writing <code>post('/login', ...)</code>, Wayfinder automatically generates TypeScript definitions for your routes:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">import</span> { store } <span class="hljs-keyword">from</span> <span class="hljs-string">'./authenticated-session-controller'</span>
</code></pre>
<p>The route becomes a beautifully typed object with <code>method</code> and <code>URL</code> properties. Change your PHP route from POST to PUT? It automatically reflects in your client-side code.</p>
<p><strong>Inertia Gets a Type Safety Makeover</strong></p>
<p>Wayfinder understands your entire Inertia setup:</p>
<ul>
<li>What props your components receive</li>
<li>What shape your request data should be</li>
<li>What your server expects when you submit forms</li>
</ul>
<p>Change a server-side form request, and boom—TypeScript definitions update automatically. Add a required <code>active</code> boolean field to your PHP validation? Your frontend immediately shows a red squiggly line if you forget to include it.</p>
<p><strong>Beyond Inertia: Standard HTTP Gets Love Too</strong></p>
<p>Not using Inertia? Wayfinder works with standard fetch requests too. Your analytics endpoint expects specific page values? Wayfinder provides autocomplete for valid values based on your validation rules.</p>
<p><strong>Laravel Ranger: The Engine Behind the Magic</strong></p>
<p>Wayfinder is built on <strong>Laravel Ranger</strong>, a more general-purpose package that walks your Laravel app and discovers "interesting things"—models, enums, validation rules—and converts them into well-typed DTOs.</p>
<p>Want auto-generated JSON schemas that perfectly match your PHP models? Ranger's got you covered.</p>
<p><strong>Timeline:</strong></p>
<ul>
<li><strong>Echo and streaming hooks</strong>: Available now</li>
<li><strong>Inertia form component</strong>: Coming in a couple weeks</li>
<li><strong>Wayfinder and Ranger</strong>: Public beta "very, very soon" with full release fall 2025</li>
</ul>
<hr>
<h2 id="laravel-ai">🤖 Laravel AI</h2>
<p><img src="https://cdn.devdojo.com/images/july2025/screenshot_41_15_cropped.png" alt="screenshot_41_15_cropped"></p>
<p>Ashley Hindle introduced how Laravel is stepping into the world of AI. LLMs are decent at PHP and Laravel, but there are still gaps in day-to-day development assistance.</p>
<p><strong>Laravel Boost: Your AI Coding Starter Kit</strong></p>
<p><img src="https://cdn.devdojo.com/images/july2025/screenshot_43m50s_cropped.png" alt="screenshot_43m50s_cropped"></p>
<p>Meet <strong>Laravel Boost</strong>, a Composer package that transforms AI pair programming from "maybe helpful" to "actually useful."</p>
<p><strong>Three Core Features That Matter</strong></p>
<p><strong>1. Laravel-Specific MCP Server</strong>
Your AI agent gets real Laravel superpowers:</p>
<ul>
<li>Query your database directly</li>
<li>Run code through Tinker</li>
<li>Search documentation</li>
<li>Understand your project structure</li>
</ul>
<p><strong>2. Version-Specific Documentation</strong>
Laravel has vectorized the entire ecosystem documentation, but it's version-aware. Using Laravel 10 with Inertia 1.x? You get exact docs for that combination.</p>
<p><strong>3. Laravel-Maintained AI Guidelines</strong>
Boost automatically creates configuration files for Cursor, Juni, GitHub Copilot, and Claude with carefully curated, version-specific guidelines.</p>
<p><strong>Installation: Simple as It Should Be</strong></p>
<pre><code class="hljsjavascript">composer <span class="hljs-built_in">require</span> laravel/boost --dev
php artisan boost:install
</code></pre>
<p>The interactive installer auto-detects your IDE setup, figures out which packages you're using, and discovers project-specific guidelines.</p>
<p>It automatically discovers guidelines based on your exact package versions—13 different guidelines in the demo, including Inertia V2, PHPUnit, Flux UI, and Tailwind 4.</p>
<p>Project-specific guidelines use Blade templating. Want to query the database in your guidelines? Go for it.</p>
<p><strong>The Arsenal of Tools</strong></p>
<p>The MCP server includes:</p>
<ul>
<li>List artisan commands</li>
<li>Query the database</li>
<li>Get absolute URLs from relative paths</li>
<li>Search documentation</li>
<li>Browser logs</li>
<li>Application info</li>
</ul>
<p><strong>Before and After: Night and Day</strong></p>
<p><strong>Without Boost</strong>: AI creates functional code that would get rejected in code review.</p>
<p><strong>With Boost</strong>: AI searches documentation, finds the correct approach, and implements it properly using the right components.</p>
<p>The search docs tool makes all the difference.</p>
<p><strong>Best part?</strong> Laravel Boost launches <strong>free</strong> within two weeks of Laracon 2025.</p>
<hr>
<h2 id="laravel-design">🎨 Laravel Design</h2>
<p><img src="https://cdn.devdojo.com/images/july2025/screenshot_01_10_40_cropped.png" alt="screenshot_01_10_40_cropped"></p>
<p>David Hill explained how Laravel's design team grew from one to six people in the past year. But they're not just making things pretty—they're data mining user feedback:</p>
<blockquote>
<p>"We listen, we read, and we watch everything that you share... from your tweets to your support emails, your blog posts, your podcasts, your YouTube videos, and, yes, even the delightful Reddit posts."</p>
</blockquote>
<p>This explains why Laravel products feel like they were built by people who actually use them.</p>
<hr>
<h2 id="nightwatch-updates">🌙 Nightwatch Updates</h2>
<p>Taylor announced immediate improvements:</p>
<p><strong>New Pricing:</strong></p>
<ul>
<li>Increased included events across all plans</li>
<li>Additional events now cost <strong>50 cents per 100,000</strong> (significant reduction)</li>
</ul>
<p><strong>New Features (Available Today):</strong></p>
<ul>
<li>Cache Insights Page: See cache hits, misses, and hit rates</li>
<li>Slack Integration: Get exceptions pushed to Slack channels</li>
</ul>
<p>And yes, <strong>Nightwatch Light Mode</strong> is here—properly rethought for accessibility, not just inverted colors.</p>
<hr>
<h2 id="forge-2-0">⚒ Forge 2.0</h2>
<p><img src="https://cdn.devdojo.com/images/july2025/screenshot_82_00_cropped.png" alt="screenshot_82_00_cropped"></p>
<p>James Brooks unveiled <strong>Forge 2.0</strong>—over a year in development. Built with Vue 3, TypeScript, and Inertia, featuring a new UI that mirrors Laravel Cloud while maintaining Forge's personality.</p>
<p>The new interface focuses on contextually relevant information: recent servers, sites, deployments, and activity feeds.</p>
<p><strong>Laravel VPS: The Game Changer</strong></p>
<p><img src="https://cdn.devdojo.com/images/july2025/screenshot_1h36m30s_cropped.png" alt="screenshot_1h36m30s_cropped"></p>
<p>Taylor announced <strong>Laravel VPS</strong>—purchase servers directly from Laravel through their DigitalOcean partnership. No more API keys, no complex setup. Just sign up for Forge and start creating servers. Taylor set a goal for sub-minute server provisioning. They demolished it—servers ready in <strong>5-10 seconds</strong>. The live demo showed a fully operational server with database credentials ready almost instantly.</p>
<p>Plus, you get a free <code>.onforge.com</code> domain—no more sharing IP addresses or setting up DNS before showing clients a working URL.</p>
<p><strong>Professional-Grade Features</strong></p>
<p><strong>Zero Downtime Deployments</strong>: Your deployment runs in a new release directory, gets fully prepared, then becomes active. No more crossing fingers during deployments.</p>
<p><strong>Health Checks</strong>: After deployment, Forge automatically pings your site from multiple locations worldwide. If something's broken, you know immediately.</p>
<p><strong>Heartbeat Monitoring</strong>: Create a heartbeat, get a URL, ping it when scheduled tasks complete. If Forge doesn't hear from your scheduler, you get notified.</p>
<p><strong>Beyond Laravel: Nuxt.js Support</strong></p>
<p>The demo showed a Nuxt.js application with full SSR support, signaling Forge's expansion beyond just Laravel applications.</p>
<p><strong>The Terminal Integration That Changes Everything</strong></p>
<p>Remember SSH key management, copying IP addresses, and the whole dance just to debug something? That's dead.</p>
<p><strong>Control+Backtick drops you into a server terminal.</strong> That's it. Full auto-completion, PHP version checks, HTOP—a complete SSH session in your browser.</p>
<p>Site-level terminal access drops you right into that site's directory.</p>
<p>The collaboration features blew minds—see when team members start sessions and join them in real-time. Pair programming for server debugging.</p>
<p><strong>Timeline:</strong> All Forge updates launch in <strong>8 weeks</strong>.</p>
<hr>
<h2 id="laravel-cloud">☁️ Laravel Cloud</h2>
<p><img src="https://cdn.devdojo.com/images/july2025/screenshot_01_58_19_cropped.png" alt="screenshot_01_58_19_cropped"></p>
<p>Joe Dixon showed how Laravel Cloud is reimagining modern PHP app hosting.</p>
<p><strong>Starter Kits That Deploy Themselves</strong></p>
<p>Pick Laravel, Livewire, Vue, or React. Pick your repo name and region. Boom:</p>
<ul>
<li>GitHub repo created</li>
<li>Starter files pushed</li>
<li>App deployed</li>
<li>Auto-deploy setup complete</li>
</ul>
<p>From idea to deployed app in under a minute.</p>
<p><strong>Auto-Scaling That Actually Thinks</strong></p>
<p>Set custom CPU and memory thresholds for granular scaling control.</p>
<p>It's like having a full-time DevOps engineer who doesn't sleep or mess up.</p>
<p><strong>Managed Queue Clusters: The Horizon Alternative</strong></p>
<p>Set rules like "If jobs wait more than 5 seconds, scale up." Cloud watches CPU, memory, queue length, throughput, job wait times, and worker activity—then auto-scales vertically or horizontally in real time.</p>
<p>The demo with 2,000 jobs showed real-time scaling with replica visualization.</p>
<p><strong>WebSockets with One Click (Managed Reverb)</strong></p>
<p><img src="https://cdn.devdojo.com/images/july2025/screenshot_130_30_cropped.png" alt="screenshot_130_30_cropped"></p>
<p>Spinning up WebSocket servers:</p>
<ol>
<li>Click "Add Resource"</li>
<li>Select "WebSockets"</li>
<li>Choose connection limit</li>
<li>Click "Create"</li>
<li>Done</li>
</ol>
<p>All Reverb environment variables auto-injected, CORS handled, each app gets isolated instances sharing server infrastructure.</p>
<p><strong>Shipping Schedule</strong></p>
<p><strong>✅ Available Today:</strong></p>
<ul>
<li>Production-ready MySQL</li>
<li>Starter Kits with auto-deploy</li>
<li>Auto-scaling threshold controls</li>
</ul>
<p><strong>🕒 Coming August 12:</strong></p>
<ul>
<li>New Starter &amp; Pro plan pricing</li>
<li>Custom domains on every plan</li>
<li>Smart queue clusters</li>
</ul>
<p><strong>🍂 Coming This Fall:</strong></p>
<ul>
<li>Fully managed Reverb</li>
<li>Laravel Valkey (open-source Redis alternative)</li>
<li>Cloud API for DevOps automation</li>
<li>Role-based access control</li>
<li>Dedicated regions + enterprise compute</li>
</ul>
<hr>
<h2 id="the-wrap-up">🎬 The Wrap-Up</h2>
<p><img src="https://cdn.devdojo.com/images/july2025/wrapup.jpg" alt="wrapup"></p>
<p>Taylor closed with a statement that resonated through the entire auditorium:</p>
<blockquote>
<p>"There is no full stack experience like this. There's just nothing even close to this."</p>
</blockquote>
<p>And honestly? He's absolutely right. The combination of Laravel's developer experience, AI-powered tooling with Laravel Boost, and this level of deployment and testing automation through Laravel Cloud creates something genuinely unprecedented in web development.</p>
<p>We're not just watching Laravel evolve—we're watching the future of web development unfold before our eyes. From attribute-based bindings that eliminate boilerplate, to AI that actually understands your codebase, to infrastructure that scales itself while you sleep, Laravel continues to push the boundaries of what's possible.</p>
<p>The ecosystem has never been stronger, and if this keynote is any indication, we're just getting started. 🚀</p>
<hr>
<p><em>Want to dive deeper into any of these features? Check out the official Laravel documentation and keep an eye on the release announcements. The future is here, and it's more exciting than we ever imagined.</em></p>
]]></description>
                                                                    <author><![CDATA[Tony Lea]]></author>
                <guid>https://devdojo.com/13496</guid>
                <pubDate>Wed, 30 Jul 2025 07:48:36 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Laravel Wayfinder]]></title>
                <link>https://devdojo.com/tnylea/laravel-wayfinder</link>
                <description><![CDATA[<p>Laravel has announced a new package called <a href="https://github.com/laravel/wayfinder">Wayfinder</a>. This package will allow you to easily generate fully-typed, importable TypeScript functions for your controllers and named routes, providing devs with a seamless integration between a Laravel backend and a TypeScript frontend.</p>
<p>Let's learn how to use <strong>Wayfinder</strong> with the Laravel <a href="https://github.com/laravel/react-starter-kit">React Starter Kit</a>. First, create a new Laravel app using the following command:</p>
<pre><code class="hljsgo">laravel <span class="hljs-built_in">new</span> wayfinder-blog --react
</code></pre>
<p>Then, cd into your project, execute <code>composer run dev</code>, and open the app in your code editor. We're ready to start <em>wayfinding</em>.</p>
<h2 id="post-migration">Post Migration</h2>
<p>To test things out with Wayfinder, we'll need to create a Post model and a <code>posts</code> migration. We can do that by running:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">php</span> <span class="hljs-selector-tag">artisan</span> <span class="hljs-selector-tag">make</span><span class="hljs-selector-pseudo">:model</span> <span class="hljs-selector-tag">Post</span> <span class="hljs-selector-tag">-mf</span>
</code></pre>
<blockquote>
<p>The <code>-mf</code> flag will also create a <code>migration</code> and a <code>factory</code>.</p>
</blockquote>
<p>Here is what that migration should look like:</p>
<pre><code class="hljsxml"><span class="php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Migrations</span>\<span class="hljs-title">Migration</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Schema</span>\<span class="hljs-title">Blueprint</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Schema</span>;

<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Migration</span>
</span>{
    <span class="hljs-comment">/**
     * Run the migrations.
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">up</span><span class="hljs-params">()</span>: <span class="hljs-title">void</span>
    </span>{
        Schema::create(<span class="hljs-string">'posts'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(Blueprint $table)</span> </span>{
            $table-&gt;id();
            $table-&gt;unsignedInteger(<span class="hljs-string">'user_id'</span>);
            $table-&gt;string(<span class="hljs-string">'title'</span>);
            $table-&gt;text(<span class="hljs-string">'body'</span>)-&gt;nullable();
            $table-&gt;string(<span class="hljs-string">'image'</span>)-&gt;nullable();
            $table-&gt;string(<span class="hljs-string">'slug'</span>)-&gt;unique();
            $table-&gt;string(<span class="hljs-string">'excerpt'</span>)-&gt;nullable();
            $table-&gt;string(<span class="hljs-string">'type'</span>)-&gt;default(<span class="hljs-string">'post'</span>);
            $table-&gt;string(<span class="hljs-string">'status'</span>)-&gt;default(<span class="hljs-string">'DRAFT'</span>);
            $table-&gt;boolean(<span class="hljs-string">'active'</span>)-&gt;default(<span class="hljs-number">1</span>);
            $table-&gt;boolean(<span class="hljs-string">'featured'</span>)-&gt;default(<span class="hljs-number">0</span>);

            <span class="hljs-comment">// SEO COLUMNS</span>
            $table-&gt;string(<span class="hljs-string">'meta_title'</span>)-&gt;nullable();
            $table-&gt;string(<span class="hljs-string">'meta_description'</span>)-&gt;nullable();
            $table-&gt;text(<span class="hljs-string">'meta_schema'</span>)-&gt;nullable();
            $table-&gt;text(<span class="hljs-string">'meta_data'</span>)-&gt;nullable();

            $table-&gt;timestamps();
        });
    }

    <span class="hljs-comment">/**
     * Reverse the migrations.
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">down</span><span class="hljs-params">()</span>: <span class="hljs-title">void</span>
    </span>{
        Schema::dropIfExists(<span class="hljs-string">'posts'</span>);
    }
};
</span></code></pre>
<p>And the Factory should look like this:</p>
<pre><code class="hljsxml"><span class="php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">Database</span>\<span class="hljs-title">Factories</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">User</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Factories</span>\<span class="hljs-title">Factory</span>;

<span class="hljs-comment">/**
 * <span class="hljs-doctag">@extends</span> \Illuminate\Database\Eloquent\Factories\Factory&lt;\App\Models\Post&gt;
 */</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PostFactory</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Factory</span>
</span>{
    <span class="hljs-comment">/**
     * Define the model's default state.
     *
     * <span class="hljs-doctag">@return</span> array&lt;string, mixed&gt;
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">definition</span><span class="hljs-params">()</span>: <span class="hljs-title">array</span>
    </span>{
        <span class="hljs-keyword">return</span> [
            <span class="hljs-string">'user_id'</span> =&gt; User::first()-&gt;id,
            <span class="hljs-string">'title'</span> =&gt; fake()-&gt;sentence(),
            <span class="hljs-string">'body'</span> =&gt; fake()-&gt;paragraphs(<span class="hljs-number">4</span>, <span class="hljs-keyword">true</span>),
            <span class="hljs-string">'slug'</span> =&gt; fake()-&gt;unique()-&gt;slug()
        ];
    }
}
</span></code></pre>
<p>Now that we have our migration and Factory, we need to run our migration:</p>
<pre><code class="hljs">php artisan migrate
</code></pre>
<h2 id="adding-posts">Adding Posts</h2>
<p>Inside our PostFactory, we have a foreign key, <code>user_id</code> that references the first user in our database. We need to add a user first before we can create posts. Let's run <code>php artisan tinker</code> and run the following:</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">App\Models\User::factory()-&gt;create()
</code></pre>
<p>Next, we need to create some sample posts we can play with. Let's run <code>php artisan tinker</code> and run the following:</p>
<pre><code class="hljsphp">App\Models\Post::factory()-&gt;count(<span class="hljs-number">30</span>)-&gt;create()
</code></pre>
<h2 id="create-our-postcontroller">Create our PostController</h2>
<p>Let's create a new controller for our posts. We can do that by running:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">php</span> <span class="hljs-selector-tag">artisan</span> <span class="hljs-selector-tag">make</span><span class="hljs-selector-pseudo">:controller</span> <span class="hljs-selector-tag">PostController</span>
</code></pre>
<p>This will create a new controller in the <code>app/Http/Controllers</code> directory.</p>
<p>Let's add a few methods to our Controller:</p>
<pre><code class="hljsxml"><span class="php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Controllers</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Post</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Request</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">JsonResponse</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PostController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Controller</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">index</span><span class="hljs-params">()</span> : <span class="hljs-title">JsonResponse</span>
    </span>{
        <span class="hljs-keyword">return</span> response()-&gt;json(Post::all());
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">show</span><span class="hljs-params">(Post $post)</span> : <span class="hljs-title">JsonResponse</span>
    </span>{
        <span class="hljs-keyword">return</span> response()-&gt;json($post);
    }
}
</span></code></pre>
<h2 id="add-post-routes">Add Post Routes</h2>
<p>We need to specify the routes for our PostController. Add the following routes to your routes/web.php file:</p>
<pre><code class="hljsphp"><span class="hljs-comment">// Post routes</span>
Route::get(<span class="hljs-string">'posts'</span>, [PostController::class, <span class="hljs-string">'index'</span>])-&gt;name(<span class="hljs-string">'posts.index'</span>);
Route::get(<span class="hljs-string">'posts/{post}'</span>, [PostController::class, <span class="hljs-string">'show'</span>])-&gt;name(<span class="hljs-string">'posts.show'</span>);
</code></pre>
<h2 id="installing-wayfinder">Installing Wayfinder</h2>
<p>Let's install Wayfinder by running:</p>
<pre><code class="hljsjavascript">composer <span class="hljs-built_in">require</span> laravel/wayfinder
</code></pre>
<p>Then we need to generate our typescript definitions by running the following:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">php</span> <span class="hljs-selector-tag">artisan</span> <span class="hljs-selector-tag">wayfinder</span><span class="hljs-selector-pseudo">:generate</span>
</code></pre>
<p>Now, you'll see many new definitions if you go into your <code>resources/js/actions</code> directory. One of them will be at <code>resources/js/actions/App/Http/Controllers/PostController.ts</code>, which will look like the following:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">import</span> { queryParams, type QueryParams } <span class="hljs-keyword">from</span> <span class="hljs-string">'./../../../../wayfinder'</span>

<span class="hljs-comment">/** 
 * <span class="hljs-doctag">@see </span>\App\Http\Controllers\PostController::index
 * <span class="hljs-doctag">@see </span>app/Http/Controllers/PostController.php:11
 * <span class="hljs-doctag">@route </span>/posts
 */</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> index = <span class="hljs-function">(<span class="hljs-params">options?: { query?: QueryParams, mergeQuery?: QueryParams }, </span>) =&gt;</span> ({
    <span class="hljs-attr">url</span>: index.url(options),
    <span class="hljs-attr">method</span>: <span class="hljs-string">'get'</span>,
})

index.definition = {
    <span class="hljs-attr">methods</span>: [<span class="hljs-string">'get'</span>,<span class="hljs-string">' head'</span>],
    <span class="hljs-attr">url</span>: <span class="hljs-string">'\/posts'</span>,
}

<span class="hljs-comment">/** 
 * <span class="hljs-doctag">@see </span>\App\Http\Controllers\PostController::index
 * <span class="hljs-doctag">@see </span>app/Http/Controllers/PostController.php:11
 * <span class="hljs-doctag">@route </span>/posts
 */</span>
index.url = <span class="hljs-function">(<span class="hljs-params">options?: { query?: QueryParams, mergeQuery?: QueryParams }, </span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> index.definition.url + queryParams(options)
}

<span class="hljs-comment">/** 
 * <span class="hljs-doctag">@see </span>\App\Http\Controllers\PostController::index
 * <span class="hljs-doctag">@see </span>app/Http/Controllers/PostController.php:11
 * <span class="hljs-doctag">@route </span>/posts
 */</span>
index.get = <span class="hljs-function">(<span class="hljs-params">options?: { query?: QueryParams, mergeQuery?: QueryParams }, </span>) =&gt;</span> ({
    <span class="hljs-attr">url</span>: index.url(options),
    <span class="hljs-attr">method</span>: <span class="hljs-string">'get'</span>,
})

<span class="hljs-comment">/** 
 * <span class="hljs-doctag">@see </span>\App\Http\Controllers\PostController::index
 * <span class="hljs-doctag">@see </span>app/Http/Controllers/PostController.php:11
 * <span class="hljs-doctag">@route </span>/posts
 */</span>
index.head = <span class="hljs-function">(<span class="hljs-params">options?: { query?: QueryParams, mergeQuery?: QueryParams }, </span>) =&gt;</span> ({
    <span class="hljs-attr">url</span>: index.url(options),
    <span class="hljs-attr">method</span>: <span class="hljs-string">'head'</span>,
})

<span class="hljs-comment">/** 
 * <span class="hljs-doctag">@see </span>\App\Http\Controllers\PostController::show
 * <span class="hljs-doctag">@see </span>app/Http/Controllers/PostController.php:16
 * <span class="hljs-doctag">@route </span>/posts/{post}
 */</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> show = <span class="hljs-function">(<span class="hljs-params">args: { post: number | { id: number } } | [post: number | { id: number }] | number | { id: number }, options?: { query?: QueryParams, mergeQuery?: QueryParams }, </span>) =&gt;</span> ({
    <span class="hljs-attr">url</span>: show.url(args, options),
    <span class="hljs-attr">method</span>: <span class="hljs-string">'get'</span>,
})

show.definition = {
    <span class="hljs-attr">methods</span>: [<span class="hljs-string">'get'</span>,<span class="hljs-string">' head'</span>],
    <span class="hljs-attr">url</span>: <span class="hljs-string">'\/posts\/{post}'</span>,
}

<span class="hljs-comment">/** 
 * <span class="hljs-doctag">@see </span>\App\Http\Controllers\PostController::show
 * <span class="hljs-doctag">@see </span>app/Http/Controllers/PostController.php:16
 * <span class="hljs-doctag">@route </span>/posts/{post}
 */</span>
show.url = <span class="hljs-function">(<span class="hljs-params">args: { post: number | { id: number } } | [post: number | { id: number }] | number | { id: number }, options?: { query?: QueryParams, mergeQuery?: QueryParams }, </span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> args === <span class="hljs-string">'string'</span> || <span class="hljs-keyword">typeof</span> args === <span class="hljs-string">'number'</span>) {
        args = { <span class="hljs-attr">post</span>: args }
    }

    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> args === <span class="hljs-string">'object'</span> &amp;&amp; !<span class="hljs-built_in">Array</span>.isArray(args) &amp;&amp; <span class="hljs-string">'id'</span> <span class="hljs-keyword">in</span> args) {
        args = { <span class="hljs-attr">post</span>: args.id }
    }

    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Array</span>.isArray(args)) {
        args = {
            <span class="hljs-attr">post</span>: args[<span class="hljs-number">0</span>],
        }
    }

    <span class="hljs-keyword">const</span> parsedArgs = {
        <span class="hljs-attr">post</span>: <span class="hljs-keyword">typeof</span> args.post === <span class="hljs-string">'object'</span>
            ? args.post.id
            : args.post,
    }

    <span class="hljs-keyword">return</span> show.definition.url
            .replace(<span class="hljs-string">'{post}'</span>, parsedArgs.post.toString())
            .replace(<span class="hljs-regexp">/\/+$/</span>, <span class="hljs-string">''</span>) + queryParams(options)
}

<span class="hljs-comment">/** 
 * <span class="hljs-doctag">@see </span>\App\Http\Controllers\PostController::show
 * <span class="hljs-doctag">@see </span>app/Http/Controllers/PostController.php:16
 * <span class="hljs-doctag">@route </span>/posts/{post}
 */</span>
show.get = <span class="hljs-function">(<span class="hljs-params">args: { post: number | { id: number } } | [post: number | { id: number }] | number | { id: number }, options?: { query?: QueryParams, mergeQuery?: QueryParams }, </span>) =&gt;</span> ({
    <span class="hljs-attr">url</span>: show.url(args, options),
    <span class="hljs-attr">method</span>: <span class="hljs-string">'get'</span>,
})

<span class="hljs-comment">/** 
 * <span class="hljs-doctag">@see </span>\App\Http\Controllers\PostController::show
 * <span class="hljs-doctag">@see </span>app/Http/Controllers/PostController.php:16
 * <span class="hljs-doctag">@route </span>/posts/{post}
 */</span>
show.head = <span class="hljs-function">(<span class="hljs-params">args: { post: number | { id: number } } | [post: number | { id: number }] | number | { id: number }, options?: { query?: QueryParams, mergeQuery?: QueryParams }, </span>) =&gt;</span> ({
    <span class="hljs-attr">url</span>: show.url(args, options),
    <span class="hljs-attr">method</span>: <span class="hljs-string">'head'</span>,
})

<span class="hljs-keyword">const</span> PostController = { index, show }

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> PostController
</code></pre>
<h2 id="using-our-new-definitions">Using our new definitions</h2>
<p>We can now refer to this PostController by importing it into any page. Let's open up our <code>resources/js/pages/dashboard.ts</code> page and add the following:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">import</span> { show } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/actions/App/Http/Controllers/PostController"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Dashboard</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">console</span>.log(show(<span class="hljs-number">1</span>));
    <span class="hljs-keyword">return</span> (
        ...
        ...
</code></pre>
<p>If you visit your dashboard page now, you should see the following console output:</p>
<pre><code class="hljscss">{
    <span class="hljs-attribute">url</span>: <span class="hljs-string">'/posts/1'</span>, 
    method: <span class="hljs-string">'get'</span>
}
</code></pre>
<p>This will define how we can <code>get</code> a post from our <code>show</code> method inside our PostController. If we ever decide to change a route in our application, for instance, if we were to change the following:</p>
<pre><code class="hljsphp">Route::get(<span class="hljs-string">'posts/{post}'</span>, [PostController::class, <span class="hljs-string">'show'</span>])-&gt;name(<span class="hljs-string">'posts.show'</span>);
</code></pre>
<p>To be singular <code>post/</code> instead of <code>posts/</code>:</p>
<pre><code class="hljsphp">Route::get(<span class="hljs-string">'post/{post}'</span>, [PostController::class, <span class="hljs-string">'show'</span>])-&gt;name(<span class="hljs-string">'posts.show'</span>);
</code></pre>
<p>We don't have to do anything in our application except run the <code>php artisan wayfinder:generate</code> again. Wayfinder will automatically update the definitions for us.</p>
<h2 id="forms-with-wayfinder">Forms with Wayfinder</h2>
<p>If you are using a traditional form to submit data, Wayfinder can make your life a lot easier and allow you to destructure the <code>action</code> and <code>method</code> for your form, like the following:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> {<span class="hljs-attr">...store.form</span>()} <span class="hljs-attr">className</span>=<span class="hljs-string">"space-y-4"</span>&gt;</span>
</code></pre>
<p>Let's cover an example of how we can use this. First, we'll need to add a new route to our <code>routes/web.php</code> file:</p>
<pre><code class="hljsphp"><span class="hljs-comment">// create a post route to create new posts</span>
Route::post(<span class="hljs-string">'post'</span>, [PostController::class, <span class="hljs-string">'store'</span>])-&gt;name(<span class="hljs-string">'posts.store'</span>);
</code></pre>
<p>We will also need to create the <code>store()</code> method inside our PostController. Add the following to your <code>PostController.php</code> file:</p>
<pre><code class="hljsphp"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">store</span><span class="hljs-params">(Request $request)</span> : <span class="hljs-title">JsonResponse</span>
</span>{
    $post = Post::create($request-&gt;all());
    <span class="hljs-keyword">return</span> response()-&gt;json($post);
}
</code></pre>
<p>You will obviously want to validate the input for the new post, but we will keep it simple for this example. Next, we want to regenerate our typescript definitions, but this time, we want to specify that we also want to include <code>form</code> variants:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">php</span> <span class="hljs-selector-tag">artisan</span> <span class="hljs-selector-tag">wayfinder</span><span class="hljs-selector-pseudo">:generate</span> <span class="hljs-selector-tag">--with-form</span>
</code></pre>
<p>Now, we can call <code>store.form()</code> to get the form <code>action</code> and <code>method</code>. Here is an example of the full <code>dashboard.tsx</code> with the form:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">import</span> AppLayout <span class="hljs-keyword">from</span> <span class="hljs-string">'@/layouts/app-layout'</span>;
<span class="hljs-keyword">import</span> { type BreadcrumbItem } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/types'</span>;
<span class="hljs-keyword">import</span> { Head, useForm, usePage } <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/react'</span>;
<span class="hljs-keyword">import</span> { store } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/actions/App/Http/Controllers/PostController"</span>;

<span class="hljs-keyword">const</span> breadcrumbs: BreadcrumbItem[] = [
    {
        <span class="hljs-attr">title</span>: <span class="hljs-string">'Dashboard'</span>,
        <span class="hljs-attr">href</span>: <span class="hljs-string">'/dashboard'</span>,
    },
];

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Dashboard</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> props = usePage().props;
    <span class="hljs-keyword">const</span> { data, setData, processing, errors, reset } = useForm({ <span class="hljs-attr">user_id</span>: <span class="hljs-number">1</span>,  <span class="hljs-attr">title</span>: <span class="hljs-string">''</span>, <span class="hljs-attr">body</span>: <span class="hljs-string">''</span>, <span class="hljs-attr">slug</span>: <span class="hljs-string">''</span> });
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">AppLayout</span> <span class="hljs-attr">breadcrumbs</span>=<span class="hljs-string">{breadcrumbs}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Head</span> <span class="hljs-attr">title</span>= <span class="hljs-string">"Dashboard"</span>/&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex h-full flex-1 flex-col gap-4 rounded-xl p-4"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-2xl font-bold mb-4"</span>&gt;</span>Create New Post<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
                
                <span class="hljs-tag">&lt;<span class="hljs-name">form</span> {<span class="hljs-attr">...store.form</span>()} <span class="hljs-attr">className</span>=<span class="hljs-string">"space-y-4"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"hidden"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"user_id"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{data.user_id}</span> /&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"hidden"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"_token"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{props.csrf_token}</span> /&gt;</span>
                    
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"space-y-2"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> 
                            <span class="hljs-attr">placeholder</span>= <span class="hljs-string">"Title"</span>
                            <span class="hljs-attr">name</span>= <span class="hljs-string">"title"</span>
                            <span class="hljs-attr">value</span>=<span class="hljs-string">{data.title}</span>
                            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{e</span> =&gt;</span> setData('title', e.target.value)}
                            className="w-full p-2 border rounded-md"
                            required
                        /&gt;
                        {errors.title &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500"</span>&gt;</span>{errors.title}<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">className</span>=<span class="hljs-string">"space-y-2"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> 
                            <span class="hljs-attr">placeholder</span>= <span class="hljs-string">"Body"</span>
                            <span class="hljs-attr">name</span>= <span class="hljs-string">"body"</span>
                            <span class="hljs-attr">value</span>=<span class="hljs-string">{data.body}</span>
                            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{e</span> =&gt;</span> setData('body', e.target.value)}
                            className="w-full p-2 border rounded-md h-32"
                            required
                        /&gt;
                        {errors.body &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500"</span>&gt;</span>{errors.body}<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">className</span>=<span class="hljs-string">"space-y-2"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> 
                            <span class="hljs-attr">placeholder</span>= <span class="hljs-string">"Slug"</span>
                            <span class="hljs-attr">name</span>= <span class="hljs-string">"slug"</span>
                            <span class="hljs-attr">value</span>=<span class="hljs-string">{data.slug}</span>
                            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{e</span> =&gt;</span> setData('slug', e.target.value)}
                            className="w-full p-2 border rounded-md"
                            required
                        /&gt;
                        {errors.slug &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500"</span>&gt;</span>{errors.slug}<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">button</span> 
                        <span class="hljs-attr">type</span>= <span class="hljs-string">"submit"</span>
                        <span class="hljs-attr">className</span>=<span class="hljs-string">"px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors"</span>
                        <span class="hljs-attr">disabled</span>=<span class="hljs-string">{processing}</span>
                    &gt;</span>
                        {processing ? 'Creating...': 'Create Post'}
                    <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">AppLayout</span>&gt;</span></span>
    );
}
</code></pre>
<p><strong>Quick tip</strong>: We needed the <code>csrf_token</code> for our form, so you will need to add the <code>csrf_token()</code> to the <code>HandleInertiaRequests</code> middleware to make it available via <code>usePage().props</code></p>
<p>If you visit the <code>/dashboard</code> page, you'll see a simple Post form that allows you to create a new post.</p>
<p><img src="https://cdn.devdojo.com/images/april2025/CleanShot%202025-04-02%20at%2020.31.24@2x.png" alt="CleanShot 2025-04-02 at 20.31.24@2x.png"></p>
<h2 id="conclusion">Conclusion</h2>
<p>Laravel Wayfinder is a game-changer for bridging the gap between your backend routes and your frontend code. By automatically generating fully-typed TypeScript functions for your Laravel controllers and routes, it eliminates guesswork, reduces errors, and ensures your frontend stays in sync with your backend—effortlessly. Whether you're building forms or fetching data, Wayfinder brings a new level of developer experience to full-stack Laravel apps. Be sure to try <a href="https://github.com/laravel/wayfinder">Wayfinder</a> in your next project and enjoy type-safe routing magic that just works. 🚀</p>
]]></description>
                                                            <category>laravel</category>
                                            <category>TypeScript</category>
                                            <category>wayfinder</category>
                                                    <author><![CDATA[Tony Lea]]></author>
                <guid>https://devdojo.com/12933</guid>
                <pubDate>Wed, 02 Apr 2025 17:34:30 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Best way to create a React app 2025]]></title>
                <link>https://devdojo.com/tnylea/best-way-to-create-a-react-app-2025</link>
                <description><![CDATA[<p>React is one of the most popular front-end frameworks, even in 2025. So, what's the best way to install a React application?</p>
<h2 id="installing-react-with-vite">Installing React with Vite</h2>
<p>The simplest way today is to use <a href="https://vite.dev">Vite</a>. You'll need to have NodeJS installed on your machine, and then you can run the following command:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">npm</span> <span class="hljs-selector-tag">create</span> <span class="hljs-selector-tag">vite</span><span class="hljs-keyword">@latest</span>
</code></pre>
<p>This will give you a prompt that looks like the following:</p>
<p><img src="https://cdn.devdojo.com/images/march2025/Screenshot%202025-03-22%20at%208.15.04%E2%80%AFAM.png" alt="Screenshot 2025-03-22 at 8.15.04 AM.png"></p>
<p>The first prompt will ask you which folder you want to install your new React application in. Then, it will prompt you which framework you want to use. Select React.</p>
<p>Next, you'll see another prompt that asks you which variant of React you want to use:</p>
<ul>
<li>TypeScript</li>
<li>TypeScript + SWC</li>
<li>JavaScript</li>
<li>JavaScript + SWC</li>
<li>React Router v7</li>
</ul>
<p><img src="https://cdn.devdojo.com/images/march2025/CleanShot%202025-03-22%20at%2008.10.45@2x.png" alt="CleanShot 2025-03-22 at 08.10.45@2x.png"></p>
<p>If you are more familiar with Javascript, select the Javascript version. Otherwise, choose Typescript if you want to use Typescript. The <strong>SWC</strong> stands for <strong>Speedy Web Compiler</strong>, a new and faster compiler built in Rust. It's up to you if you want to use it. Select the variant you wish to use.</p>
<p>In this case, I'm going to select the Javascript version. After selecting the version you want, you'll see a message that says the following:</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">cd <span class="hljs-keyword">my</span>-project
npm install
npm run dev
</code></pre>
<p>Go ahead and run those commands, and you'll see a message that looks like the following:</p>
<p><img src="https://cdn.devdojo.com/images/march2025/Screenshot%202025-03-22%20at%208.21.52%E2%80%AFAM.png" alt="Screenshot 2025-03-22 at 8.21.52 AM.png"></p>
<p>Then, visit that URL in the browser (in my case, https://localhost:5173), and you'll see a new React app in front of you.</p>
<p><img src="https://cdn.devdojo.com/images/march2025/CleanShot%202025-03-22%20at%2008.22.44@2x.png" alt="CleanShot 2025-03-22 at 08.22.44@2x.png"></p>
<p>Time to start building 🛠️</p>
<h2 id="installing-react-with-laravel">Installing React with Laravel</h2>
<p>Another option for installing React with a super-powered back-end is using React in a <a href="https://laravel.com">Laravel application</a>.</p>
<p>Before installing a React+Laravel application, you must download Herd on your machine: <a href="https://herd.laravel.com">https://herd.laravel.com</a>. After installing, move into your <code>Herd</code> directory, which will be <code>~/Herd</code> on Mac and <code>c:&gt;Herd</code> on Windows.</p>
<p>Next, run the following command:</p>
<pre><code class="hljsgo">laravel <span class="hljs-built_in">new</span> my-project --using=tnylea/react-starter
</code></pre>
<p>This will install a React+Laravel application into a folder named <code>my-project</code>. After the installation is completed, you'll see the following message:</p>
<p><img src="https://cdn.devdojo.com/images/march2025/Screenshot%202025-03-22%20at%208.28.40%E2%80%AFAM.png" alt="Screenshot 2025-03-22 at 8.28.40 AM.png"></p>
<p>Be sure to <strong>cd</strong> into your directory and run <code>composer run dev</code>. Now you'll be able to visit HTTP://my-project.test in your browser, and you'll see the following page:</p>
<p><img src="https://cdn.devdojo.com/images/march2025/Screenshot%202025-03-22%20at%208.29.37%E2%80%AFAM.png" alt="Screenshot 2025-03-22 at 8.29.37 AM.png"></p>
<p>And now it's time to start building your new masterpiece.</p>
<h2 id="conclusion">Conclusion</h2>
<p>There are many ways to install a React application. Choose the method that works best for you. I recommend installing React with Laravel, but if you do not need a back-end, go ahead and use Vite.</p>
]]></description>
                                                            <category>react</category>
                                            <category>react.js</category>
                                            <category>install react</category>
                                                    <author><![CDATA[Tony Lea]]></author>
                <guid>https://devdojo.com/12900</guid>
                <pubDate>Sat, 22 Mar 2025 05:07:38 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to Get Headless CMS in Next.js with One Line]]></title>
                <link>https://devdojo.com/anmolbaranwal/how-to-get-headless-cms-in-nextjs-with-one-line</link>
                <description><![CDATA[<p>Over the years, developers have realized the power of Headless CMS.</p>
<p>Using a solid CMS with frameworks like Next.js can make a lot of difference if you're managing a lot of content.</p>
<p>While big names dominate the space, today we will explore BCMS and how to use it with Next.js. Plus, some starter templates to get you started.</p>
<p>Let's jump in.</p>
<hr>
<h2 id="what-is-covered">What is covered?</h2>
<p>In a nutshell, we are covering these topics in detail.</p>
<ol>
<li>What is BCMS and why should you use it?</li>
<li>A step-by-step guide on how to get started with BCMS and Next.js.</li>
<li>Starter templates you can use.</li>
</ol>
<p>If you want to explore yourself, please read the <a href="https://thebcms.com/docs/integrations/next-js">docs</a>.</p>
<hr>
<h2 id="toc-1-what-is-bcms-and-why-should-you-use-it">1. What is BCMS and why should you use it?</h2>
<p>BCMS is one of the underrated options as a headless CMS for building Next.js projects. It even provides flexible content modeling for other tech stacks including <code>Astro</code>, <code>Gatsby.js</code>, <code>Nuxt.js</code> and <code>Svelte</code>.</p>
<p>You also get:</p>
<p>✅ Option for Global CDN.</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>✅ Component based content modeling.</p>
<p>✅ Flexible content for multilingual sites.</p>
<p>✅ 10+ input options with detailed permissions.</p>
<p>You can do all sorts of stuff like handling forms, HR tools, sending e-mails, custom reporting, all through BCMS using <code>webhooks</code>, <code>functions</code> and <code>cron jobs</code>.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0hjn07274yx4hg99kj14.png" alt="bcms"></p>
<h3 id="before-we-proceed-let-s-understand-what-headless-cms-means">🎯 Before we proceed, let's understand what Headless CMS means.</h3>
<p>A headless CMS simply allows you to manage content in one place and deploy it on any digital channel.</p>
<p>In short, it's just any backend application, which is 100% built upon APIs. Headless means frontend less body which can be attached to any <code>frontend</code> or <code>Head</code>.</p>
<p>If you're interested to know more, you can check the <a href="https://www.storyblok.com/tp/headless-cms-explained">article by StoryBlok</a> and <a href="https://www.builder.io/blog/headless-cms">The Ultimate Guide to Headless CMS</a> by the Builder.io team. It has visual diagrams, use cases, merits and a lot more.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zee5pwykw5awvtukkz3z.png" alt="headless cms vs traditional cms"></p>
<h3 id="why-should-you-use-bcms">Why should you use BCMS?</h3>
<p>There are many useful features that make BCMS worth it. Let's explore some of them.</p>
<p>✅ BCMS lets you store content in flexible inputs like dropdowns, files, numbers and rich text, all built to work smoothly with Next.js applications.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/seftqrlqdbx2cav17auc.gif" alt="input types"></p>
<p>✅ You can easily upload and manage any media in your Next.js project using BCMS's organized folder system (much better than the others). Read the <a href="https://thebcms.com/docs/inside-bcms/media">docs</a>.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ike9y4abx8q5zvjs8gs.png" alt="media manager"></p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ejfwyp1jsvtr6zubtms.png" alt="media in dashboard"></p>
<figcaption>media in dashboard</figcaption>
<p>✅ You also get dynamic components for your blog or e-commerce site. They are called <code>Widgets</code> in BCMS which are reusable building blocks that are used inside of the entry's content area. Read the <a href="https://thebcms.com/docs/inside-bcms/widgets">docs</a>.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ya7pebjd2c3frfhtuy28.jpeg" alt="widgets"></p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3bu6cvnucua2d6y9m6vz.png" alt="widgets in dashboard"></p>
<figcaption>widgets in dashboard</figcaption>
<p>✅ There is also the concept of <code>Groups</code> which helps you to define reusable content structures, like author profiles or addresses, to be easily queried in your Next.js application.</p>
<p>You can use them in templates, widgets or even inside other groups. Read the <a href="https://thebcms.com/docs/inside-bcms/groups">docs</a>.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nbsvl15lax6hk1oo14o3.jpeg" alt="groups"></p>
<p>✅ To write multilingual content in BCMS, all you need to do is just click <code>add</code> and choose from nearly 100 languages.</p>
<p>English is the default and can’t be deleted, but other languages can be removed which will also remove all associated data with that language.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2f9rz7u7zcn6jwx98pou.png" alt="multilingual websites"></p>
<p>✅ There is also the concept of <code>entry</code> which is like a single document or item created from a predefined template.</p>
<p>The template decides what details each entry should have. These details, called <code>properties</code>, appear in a section called <code>meta</code>. Every entry also has a content area where you can write text and add interactive elements.</p>
<p>For example, if you create a Blog template, you can set properties like author, category, tags, and publish date. Then, whenever you add a new blog post (<code>entry</code>), these fields will be ready to fill.</p>
<p>You can duplicate the entry, delete it and even set the entry status to indicate when a blog post is ready for publishing. Read the <a href="https://thebcms.com/docs/inside-bcms/entries">docs</a> to know more.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wy7fzc0zzj4zqwlboudb.png" alt="entries"></p>
<p>You can read about all the features on <a href="https://thebcms.com/nextjs-cms">thebcms.com/nextjs-cms</a>.</p>
<p>By the way, <a href="https://github.com/bcms/cms">BCMS is open source</a> with with hundreds of stars on GitHub. You can find jobs, plugins and more from the readme.</p>
<p>Now that we have understood the unique concepts of BCMS, it's time to integrate BCMS with Next.js in the next section.</p>
<hr>
<h2 id="toc-2-a-step-by-step-guide-on-how-to-get-started-with-bcms-and-next-js">2. A step-by-step guide on how to get started with BCMS and Next.js.</h2>
<p>We will keep it simple and easy to understand even if you know nothing about the concepts. You can read the <a href="https://thebcms.com/docs/integrations/next-js">docs</a> if you're interested in following everything from scratch.</p>
<p>The first thing you need to install is the BCMS CLI, by running the following command.</p>
<pre><code class="hljsruby">
npx @thebcms/cli create <span class="hljs-keyword">next</span>

</code></pre>
<p>You will get the option to install the package and authenticate with BCMS in case you are not.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6t74g74lnzpk7ejd6syj.png" alt="authorize bcms github"></p>
<p>Once you log in, this is how the dashboard looks. It's available at <a href="https://app.thebcms.com/">app.thebcms.com/</a>.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vyilw6h53yv8iysqr38l.png" alt="dashboard"></p>
<p>This sets up a new Next.js project and lets you pick a starter template based on your needs. You can also start from scratch, but to keep things simple, I will go with the blog starter.</p>
<p>This will set up a new nextjs project and it will give you options to select a starter project (based on your use case). There is also the option to start from scratch but for the sake of easiness, I'm selecting the blog starter.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7vsz0fmb8dyl485ejc7j.png" alt="cli full"></p>
<p>This is how the directory structure looks.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jshe38yrhh3rvb2xuvu9.png" alt="directory structure"></p>
<p>In the root of your project, you will find <code>bcms.config.cjs</code> which is the main configuration file.</p>
<pre><code class="hljsphp">
<span class="hljs-comment">/**

* <span class="hljs-doctag">@type</span> {import('<span class="hljs-doctag">@thebcms</span>/cli/config').BCMSConfig}

*/</span>

module.exports = {

&nbsp; &nbsp;&nbsp;client: {

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;orgId: process.env.BCMS_ORG_ID,

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;instanceId: process.env.BCMS_INSTANCE_ID,

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;apiKey: {

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;id: process.env.BCMS_API_KEY_ID,

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;secret: process.env.BCMS_API_KEY_SECRET,

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;},

&nbsp; &nbsp;&nbsp;},

};

</code></pre>
<p>You will see the codebase pre-filled with some content, a <code>.env</code> file, and the necessary BCMS config.</p>
<p>In the dashboard, you can explore settings if you want to adjust functions, webhooks, dependencies or environment variables.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/43v1141sb6ldpt857dt7.png" alt="settings dashboard"></p>
<figcaption>settings dashboard</figcaption>
<p>If you skip the starter template, you will need to create a new <code>API Key</code> under settings. You will get the option to allow necessary permissions once you create entries in the template. Don't worry if this sounds confusing, we will go over it soon.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6fvtsootpjiqh78hs68r.png" alt="api key"></p>
<p>You just need to install the dependencies and run the server using the following commands.</p>
<pre><code class="hljs">
npm i

npm run dev

</code></pre>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qhxn5jqk1dyrclgpcgi3.png" alt="npm run dev"></p>
<p>The <code>bcms --pull types --lng ts</code> command starts the <code>BCMS CLI</code> and pulls types from BCMS, saving them in <code>/bcms/types/</code>. These types work for both JavaScript and TypeScript.</p>
<p>Then, go to <code>http://localhost:3000</code> to view the locally hosted version. Here's how it looks.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rnzeck42c6mlxa0fawez.png" alt="localhost deployed"></p>
<p>Congrats! 🎉</p>
<p>You have just integrated BCMS with Next.js, it's that simple!</p>
<p>Let's study more about what is involved. Head over to the BCMS dashboard and look for the <code>Templates</code> option in the sidebar.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/md00dc0gl9ls3wgf40qp.png" alt="templates"></p>
<p>You'll find a Blog template already created, with six properties: title, slug, date, description, cover_image, and category. Each property type is displayed as a badge (media, string, date).</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mc1j91gp5cntsdppsjcj.png" alt="six properties under template blog"></p>
<p>You can easily update any properties based on your use case.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gu2d4pmauarvgbu8h42p.png" alt="update any properties"></p>
<p>You can also create a template from scratch as shown.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zx9gd1imr07a7wbafkxj.png" alt="create template from scratch"></p>
<figcaption>create template from scratch</figcaption>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eub9tdkqpflq1frh4dqn.png" alt="add properties in a template"></p>
<figcaption>add properties in a template</figcaption>
<p>Once your template is ready, you will need to create entries for it. In this case, the <code>Blog template</code> already has four entries.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9boodnjr84ia464m5tb8.png" alt="entries"></p>
<p>Each entry contains all the necessary details.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rhipcxhif2mhldbnyjx9.png" alt="each entry has necessary details"></p>
<p>When creating a new entry, it will automatically include the required fields based on your template properties.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/81zl0wsdbg3ow9q0yfc6.png" alt="new entry"></p>
<p>Similarly, for the cover image, just upload and select it accordingly.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aqokddsggg5gwxub00rr.png" alt="media cover image"></p>
<p>Once you refresh your local setup, the updates will appear.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aqpmiyzagwr8nankb1wz.png" alt="new entry shown accordingly"></p>
<p>The entry will be accessible at the route based on the <code>slug URL</code> you set in the dashboard.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cu8ghh9qa8qvy9kyb49t.png" alt="glug blog inner page"></p>
<p>Let's talk about the codebase.</p>
<p>First, the BCMS Client is initialized by creating <code>bcms.ts</code> inside the <code>/src</code> directory.</p>
<p>The main logic for fetching and displaying data from BCMS is in <code>/src/app/page.tsx</code>.</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { bcms } <span class="hljs-keyword">from</span> <span class="hljs-string">'./bcms-client'</span>;
<span class="hljs-keyword">import</span> { BlogEntry, BlogEntryMetaItem } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../bcms/types/ts'</span>;
<span class="hljs-keyword">import</span> { Metadata } <span class="hljs-keyword">from</span> <span class="hljs-string">'next'</span>;
<span class="hljs-keyword">import</span> BlogCard <span class="hljs-keyword">from</span> <span class="hljs-string">'@/components/blog/Card'</span>;
<span class="hljs-keyword">import</span> Tag <span class="hljs-keyword">from</span> <span class="hljs-string">'@/components/Tag'</span>;

<span class="hljs-keyword">const</span> pageTitle = <span class="hljs-string">'Blogs - Simple Blog'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> metadata: Metadata = {
    <span class="hljs-attr">title</span>: pageTitle,
    <span class="hljs-attr">openGraph</span>: {
        <span class="hljs-attr">title</span>: pageTitle,
    },
    <span class="hljs-attr">twitter</span>: {
        <span class="hljs-attr">title</span>: pageTitle,
    },
};

<span class="hljs-keyword">const</span> HomePage: React.FC = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> blogs = (<span class="hljs-keyword">await</span> bcms.entry.getAll(<span class="hljs-string">'blog'</span>)) <span class="hljs-keyword">as</span> BlogEntry[];

    <span class="hljs-keyword">const</span> items = blogs.map(<span class="hljs-function">(<span class="hljs-params">blog</span>) =&gt;</span> {
        <span class="hljs-keyword">return</span> blog.meta.en <span class="hljs-keyword">as</span> BlogEntryMetaItem;
    });
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-24 md:py-32"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col gap-6 items-center text-center mb-20 md:mb-[120px]"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">Tag</span> <span class="hljs-attr">size</span>=<span class="hljs-string">"lg"</span>&gt;</span>Hi, I’m Anmol 👋<span class="hljs-tag">&lt;/<span class="hljs-name">Tag</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-4xl font-bold leading-none md:text-5xl"</span>&gt;</span>
                        This is my blog
                    <span class="hljs-tag">&lt;/<span class="hljs-name">h1</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">className</span>=<span class="hljs-string">"grid grid-cols-1 gap-12 max-w-[1040px] mx-auto"</span>&gt;</span>
                        {items.map((item, index) =&gt; {
                            return <span class="hljs-tag">&lt;<span class="hljs-name">BlogCard</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span> <span class="hljs-attr">blog</span>=<span class="hljs-string">{item}</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">div</span>&gt;</span></span>
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> HomePage;
</code></pre>
<p>The BlogPage route is defined in <code>src/app/blog/[slug]/page.tsx</code>.</p>
<p>Core components like <code>ContentManager</code> and <code>Tag</code> are inside the <code>src/app/components</code> directory.</p>
<p>It's simple to understand if you're aware of Next.js concepts.</p>
<hr>
<h2 id="toc-3-starter-templates-you-can-use">3. Starter templates you can use.</h2>
<p>Learning the concepts is great, but having everything pre-built for different use cases is amazing. Here are some starter templates that might save you time and effort.</p>
<p>✅ <a href="https://thebcms.com/starters/personal">Personal website</a></p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/24goen97m2f6ychxt648.gif" alt="portfolio"></p>
<p>This is one of the most decent portfolio templates I've come across. It’s clean, unique and well-structured, with some really good sections.</p>
<p>Built with Next.js and BCMS, this template offers a great starting point for developers. Read this <a href="https://thebcms.com/starters/personal">tutorial</a> if you want to explore everything that is involved.</p>
<p>You can check the <a href="https://github.com/bcms/starters/tree/master/next/personal">GitHub Repository</a> and <a href="https://personal-starter.thebcms.com/">live demo</a>.</p>
<p>✅ <a href="https://thebcms.com/starters/e-commerce">E-Commerce site</a></p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jrgijdwrvuq1oqesrejr.png" alt="e-commerce"></p>
<p>This is a <code>next.js</code> e-commerce template that comes with analytics, multilingual support and more.</p>
<p>If you want a detailed breakdown including templates, groups, elements involved and the step-by-step process to make this, read this <a href="https://thebcms.com/starters/e-commerce">guide</a>.</p>
<p>You can check the <a href="https://github.com/bcms/starters/tree/master/next/e-commerce">GitHub Repository</a> and <a href="https://e-commerce-starter.thebcms.com/">live demo</a>.</p>
<p>✅ <a href="https://github.com/bcms/starters/tree/master/next/podcast">Podcast website</a></p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0lvoq83lxrkiz2chpb29.png" alt="podcast website"></p>
<p>If you're planning to launch a podcast or audio-focused platform, this template is a great choice.</p>
<p>The official <a href="https://thebcms.com/starters/podcast">tutorial</a> is for <code>Gatsby</code>, but the GitHub repository provides a Next.js version as well.</p>
<p>You can check the <a href="https://github.com/bcms/starters/tree/master/next/podcast">GitHub Repository</a> and <a href="https://podcast-starter.thebcms.com/">live demo</a>.</p>
<p>✅ <a href="https://thebcms.com/starters/job-board">Job board</a></p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b3t0gsdl4j2l7fvwkdqk.png" alt="job board"></p>
<p>This is for people looking to build a job board or a career page for an organization.</p>
<p>The official <a href="https://thebcms.com/starters/job-board">tutorial</a> is for <code>Nuxt</code> but you can find the GitHub repository that includes a Next.js version as well.</p>
<p>You can check the <a href="https://github.com/bcms/starters/tree/master/next/job-board">GitHub Repository</a> and <a href="https://job-board-starter.thebcms.com/">live demo</a>.</p>
<p>BCMS provides 10+ templates across different frameworks like <code>Astro</code>, <code>Gatsby</code>, <code>Next</code> and <code>Nuxt</code>. You can find all the samples on the <a href="https://github.com/bcms/starters">official BCMS repository</a> and the official website at <a href="https://thebcms.com/starters">thebcms.com/starters</a>.</p>
<p>Other available templates include an agency website, job board, conference starter, recipe website and more. Each of these websites is really unique which makes things a lot better.</p>
<p>If you're exploring other options, you can check Sanity, Contentful, Payload and Strapi.</p>
<hr>
<p>I'm aware that there are other decent options out there but BCMS has its own perks.</p>
<p>Underrated tools can sometimes be the best fit for your workflow.</p>
<p>Let me know if you have any other ideas or if you have used any Headless CMS options.</p>
<p>Have a great day! Until next time :)</p>
<p>Check my work at <a href="https://anmolbaranwal.com/">anmolbaranwal.com</a> and reach out to <code>hi@anmolbaranwal.com</code> for any collab or sponsorships.</p>
<p>You can connect me on <a href="https://github.com/Anmol-Baranwal">GitHub</a>, <a href="https://twitter.com/Anmol_Codes">Twitter</a> and <a href="https://www.linkedin.com/in/Anmol-Baranwal/">LinkedIn</a> if you loved the content&nbsp;:)</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ylsck6b9c7ei6makpqd.gif" alt="Ending GIF waving goodbye"></p>
]]></description>
                                                            <category>programming</category>
                                            <category>developers</category>
                                            <category>nextjs</category>
                                            <category>beginners</category>
                                            <category>headless cms</category>
                                                    <author><![CDATA[Anmol Baranwal]]></author>
                <guid>https://devdojo.com/12892</guid>
                <pubDate>Fri, 21 Mar 2025 02:12:58 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Running Laravel Octane on local host with FrankenPHP &amp; Laravel Herd]]></title>
                <link>https://devdojo.com/tinahammar/running-laravel-octane-on-local-host-with-frankenphp-laravel-herd</link>
                <description><![CDATA[<p>A brief tutorial on integrating Laravel Octane with FrankenPHP and Laravel Herd, including MinIO-based S3 storage. Please not that this is a simple setup. You might want other ip or port settings...</p>
<p>(Laravel 11)</p>
<hr>
<h2 id="install-laravel-octane-frankenphp">Install Laravel Octane (FrankenPHP)</h2>
<pre><code class="hljsjavascript">composer <span class="hljs-built_in">require</span> laravel/octane
</code></pre>
<p>When prompted, say “yes” to auto-install FrankenPHP.</p>
<pre><code class="hljs">php artisan octane:install --server=frankenphp
</code></pre>
<p>To monitor file changes, install chokidar (hot reloading):</p>
<pre><code class="hljs">npm install --save-dev chokidar
</code></pre>
<h2 id="link-your-herd-valet-url-to-octane-with-https">Link your Herd (Valet) URL to Octane, with https://</h2>
<p>In your Laravel project folder, run:</p>
<pre><code class="hljsperl">herd <span class="hljs-keyword">unlink</span>
</code></pre>
<pre><code class="hljsgo">herd proxy YOUR_APP_URL.test http:<span class="hljs-comment">//127.0.0.1:8000 --secure</span>
</code></pre>
<p>(Herd will now route requests for https://YOUR_APP_URL.test to Octane on port 8000.)</p>
<h2 id="set-up-file-storage-s3-minio">Set Up File Storage (S3 / MinIO)</h2>
<p>Install the AWS S3 Flysystem driver (required even if you’re using MinIO locally):</p>
<pre><code class="hljsjavascript">composer <span class="hljs-built_in">require</span> league/flysystem-aws-s3-v3 <span class="hljs-string">"^3.0"</span> --<span class="hljs-keyword">with</span>-all-dependencies
</code></pre>
<h2 id="follow-herd-s-documentation-to-enable-minio-locally">Follow Herd’s documentation to enable MinIO locally:</h2>
<p>• https://herd.laravel.com/docs/macos/herd-pro-services/minio</p>
<p><strong>Make sure</strong> your local MinIO bucket has <strong>PUBLIC</strong> read access for development.</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>Update your <code>.env</code> with the MinIO credentials found in <code>Herd → Services → MinIO</code>:</p>
<pre><code class="hljsgo">FILESYSTEM_DISK=s3

AWS_BUCKET=
AWS_ACCESS_KEY_ID=herd
AWS_USE_PATH_STYLE_ENDPOINT=<span class="hljs-literal">true</span>
AWS_SECRET_ACCESS_KEY=secretkey
AWS_DEFAULT_REGION=
AWS_URL=
AWS_ENDPOINT=
</code></pre>
<h2 id="configure-default-storage-filament-livewire-and-spatie-media-library">Configure Default Storage, Filament, Livewire and Spatie Media Library</h2>
<p>If using Filament or Spatie Media Library, point them to the s3 disk in your <code>.env</code>:</p>
<pre><code class="hljs">FILESYSTEM_DISK=s3
FILAMENT_FILESYSTEM_DISK=s3
MEDIA_DISK=s3
</code></pre>
<p>For Livewire’s temporary file uploads (in config/livewire.php):</p>
<pre><code class="hljsphp"><span class="hljs-string">'temporary_file_upload'</span> =&gt; [
    <span class="hljs-string">'disk'</span> =&gt; env(<span class="hljs-string">'FILAMENT_FILESYSTEM_DISK'</span>, <span class="hljs-string">'local'</span>), <span class="hljs-comment">//or simply 's3'</span>
    <span class="hljs-comment">// ...</span>
],
</code></pre>
<h2 id="start-octane">Start Octane</h2>
<p>You may need to restart Herd services and clear your cache before starting Octane:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">php</span> <span class="hljs-selector-tag">artisan</span> <span class="hljs-selector-tag">octane</span><span class="hljs-selector-pseudo">:frankenphp</span> <span class="hljs-selector-tag">--watch</span>
</code></pre>
<p>If chokidar is installed, Octane will watch and hot-reload on file changes.
Now visit https://YOUR_APP_URL.test and enjoy the speed!</p>
<h2 id="troubleshooting">Troubleshooting</h2>
<ul>
<li>If you see 404s on stored images, confirm that your bucket has public read access. The same applies if you get errors when uploading files with Filament or Livewire.</li>
<li>Memory errors? Read the Laravel documentation carefully about memory leaks and try increasing the memory limit in Herd php settings</li>
</ul>
</p>]]></description>
                                                                    <author><![CDATA[ ]]></author>
                <guid>https://devdojo.com/12758</guid>
                <pubDate>Mon, 27 Jan 2025 07:51:02 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[Why PNPM? WTF?]]></title>
                <link>https://devdojo.com/tnylea/why-pnpm-wtf</link>
                <description><![CDATA[<p>If you are not regularly engaged with the JavaScript ecosystem, you may get overwhelmed by all the options available 🥵</p>
<p>In fact, even if you're trenched in the JS ecosystem, you can still feel overwhelmed. Whenever I see something like the following image:</p>
<p><img src="https://cdn.devdojo.com/images/january2025/Screenshot%202025-01-23%20at%209.25.01%E2%80%AFAM.png" alt="Screenshot 2025-01-23 at 9.25.01 AM.png"></p>
<p>I immediately disregard all options except for <strong>NPM</strong>. Why are there so many options 🤦‍♂️</p>
<p>Curiosity finally got the better of me, and I decided to figure out what the hell <strong>PNPM</strong> is and whether or not I should give a f*ck. So, here's a quick write-up to help anyone else screaming internally, "Why the hell do we need another package manager?"</p>
<h2 id="tldr">TLDR</h2>
<p><strong>PNPM</strong> is faster and saves disk space. The extra <strong>P</strong> stands for <strong>Performant</strong>. Instead of adding all the dependencies to a single project, <strong>PNPM</strong> uses a symlink strategy to share dependencies across projects. That's it! That's all you need to know.</p>
<p>But honestly, <strong>PNPM</strong> should stand for <strong>P</strong>lease <strong>N</strong>o-more <strong>P</strong>ackage <strong>M</strong>anagers. Am I right? Am I right?... (nervous chuckle)... Yeah, okay. Anyway,… moving on. 🫠</p>
<h2 id="managing-versions">Managing Versions</h2>
<p>You may be wondering, like I was, how it manages different versions. Apparently, that is one of the big selling points for why developers choose <strong>PNPM</strong>.</p>
<ul>
<li>If two projects use the same dependency version, PNPM symlinks them in a shared place.</li>
<li>If two projects need different versions of the same dependency, PNPM will store both versions in a shared place but save disk space by reusing common files.</li>
</ul>
<p>The way PNPM handles dependencies is what makes it 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>
<h2 id="should-you-use-pnpm">Should you use PNPM</h2>
<p>It's totally up to you. For now, I will stick to <strong>NPM</strong> because that's what I'm used to. If that changes, I'll be sure to update this post. Or I'll forget. Who knows?</p>
<h2 id="damn-you-javascript">Damn You Javascript</h2>
<p>This is a prime example of why web development has earned its reputation as "difficult to learn." Every week, a new library or package is released that claims to be "better," "faster," or "revolutionary," but it mainly just adds to the confusion.</p>
<h2 id="ending-on-a-positive-note">Ending on a Positive Note</h2>
<p>The JS ecosystem may be chaotic, but at least you have options for everything under the sun. So, Whether you're sticking with <strong>NPM</strong> out of habit or switching to <strong>PNPM</strong> to save some disk space, all that matters is being productive and writing code.</p>
<p>Grab your favorite package manager, write some code, and one day, JavaScript may finally chill out. Probably not, though. 😂</p>
]]></description>
                                                            <category>npm</category>
                                            <category>pnpm</category>
                                            <category>package manager</category>
                                                    <author><![CDATA[Tony Lea]]></author>
                <guid>https://devdojo.com/12748</guid>
                <pubDate>Thu, 23 Jan 2025 06:49:12 -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[Laravel custom helper functions – Fast Tips]]></title>
                <link>https://devdojo.com/inspector/laravel-custom-helper-functions-fast-tips</link>
                <description><![CDATA[<p>Helper functions are quite popular among Laravel developers. They provide access to some clever algorithms ready to be used in your code using a single statement.</p>
<p>Probably the most known helper function is <code>url()</code> that allows you to generate internal application URLs.</p>
<p>Another great helper is the <code>retry()</code> function for example: https://laravel.com/docs/11.x/helpers#method-retry</p>
<p>Easy way to retry a statement a certain number of times if it fires an exception. It’s usually used on external API calls, and in fact it was recently integrated as a method in the Laravel HTTP client.</p>
<pre><code class="hljsruby">$response = Http::<span class="hljs-keyword">retry</span>(<span class="hljs-number">3</span>, <span class="hljs-number">100</span>)-&gt;post(<span class="hljs-regexp">/* ... */</span>);
</code></pre>
<p>More about HTTP client on the dedicated article: https://inspector.dev/laravel-http-client-overview-and-monitoring/</p>
<h2 id="laravel-helper-functions-use-case">Laravel helper functions use case</h2>
<p>Sometimes we would like to automate tasks related to our business logic and encapsulating it into an helper function could be very convenient. I wrote about the implementation of array_map for associative arrays that is a perfect fit for this use case.</p>
<p>You can refer to the article to know the purpose of this function, in this context I want to make it a custom helper function to be used inside your Laravel application.</p>
<h2 id="how-to-register-new-laravel-helper-functions">How to register new Laravel helper functions</h2>
<p>First create a new file <code>helper.php</code> in the <code>app/</code> directory of your Laravel project.</p>
<p><img src="https://cdn.devdojo.com/images/october2024/helper-file-laravel-project.png" alt="Laravel directory tructure"></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>Paste the code below to declare the array_map_assoc function into the file:</p>
<pre><code class="hljsphp"><span class="hljs-keyword">if</span> (!function_exists(<span class="hljs-string">'array_map_assoc'</span>)) {
    <span class="hljs-comment">/**
     * Apply a mapping callback receiving key and value as arguments.
     * The standard array_map doesn't pass the key to the callback. But in the case of associative arrays,
     * it could be really helpful.
     *
     * array_map_assoc(function ($key, $value) {
     *  ...
     * }, $items)
     *
     * <span class="hljs-doctag">@param</span> callable $callback
     * <span class="hljs-doctag">@param</span> array $array
     * <span class="hljs-doctag">@return</span> array
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">array_map_assoc</span><span class="hljs-params">(callable $callback, array $array)</span>: <span class="hljs-title">array</span>
    </span>{
        <span class="hljs-comment">// map original array keys, and call $callable with $key and $value.</span>
        <span class="hljs-keyword">return</span> array_map(<span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">($key)</span> <span class="hljs-title">use</span> <span class="hljs-params">($callback, $array)</span></span>{
            <span class="hljs-keyword">return</span> $callback($key, $array[$key]);
        }, array_keys($array));
    }
}
</code></pre>
<p>As you can see the function declaration is wrapped by the if to avoid registering functions with the same name.</p>
<h2 id="include-the-helper-file-via-composer-s-autoload">Include the helper file via Composer's autoload</h2>
<p>Laravel doesn't automatically load custom helper files, so you need to manually instruct composer to include the file during the autoload generation.</p>
<p>In the <code>composer.json</code> file of your Laravel project find the "autoload" section, and add the “files” keyword:</p>
<pre><code class="hljsgo"><span class="hljs-string">"autoload"</span>: {
    <span class="hljs-string">"psr-4"</span>: {
        <span class="hljs-string">"App\\"</span>: <span class="hljs-string">"app/"</span>,
        <span class="hljs-string">"Database\\Factories\\"</span>: <span class="hljs-string">"database/factories/"</span>,
        <span class="hljs-string">"Database\\Seeders\\"</span>: <span class="hljs-string">"database/seeders/"</span>
    },
    <span class="hljs-string">"files"</span>: [
        <span class="hljs-string">"app/helpers.php"</span>
    ]
},
</code></pre>
<p>After adding the file, run composer dump-autoload in your terminal to refresh Composer’s autoload files.</p>
<h2 id="using-your-laravel-helper-function">Using Your Laravel Helper Function</h2>
<p>Once the helper file is loaded, you can use your custom functions anywhere in your Laravel application just like any other PHP function.</p>
<pre><code class="hljsphp">$histogram = array_map_assoc(<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($key, $value)</span> </span>{
    <span class="hljs-keyword">return</span> [
        <span class="hljs-string">'label'</span> =&gt; $key,
        <span class="hljs-string">'value'</span> =&gt; $value[<span class="hljs-string">'doc_count'</span>]
    ];
}, $data);
</code></pre>
<p>Hope it's helpful to stay organized with your codebase.</p>
<p>For more technical articles you can follow me on <a href="https://www.linkedin.com/in/valeriobarbera/">Linkedin</a> or <a href="https://x.com/Barbalerio">X</a>.</p>
<h2 id="monitor-your-php-application-for-free">Monitor your PHP application for free</h2>
<p>Inspector is a Code Execution Monitoring tool specifically designed for software developers. You don't need to install anything at the server level, just install the <strong><a href="https://github.com/inspector-apm/inspector-laravel">Laravel</a></strong> or <strong><a href="https://github.com/inspector-apm/inspector-symfony">Symfony</a></strong> package and you are ready to go.</p>
<p>If you are looking for HTTP monitoring, database query insights, and the ability to forward alerts and notifications into your preferred messaging environment, try Inspector for free. <a href="https://app.inspector.dev/register">Register your account</a>.</p>
<p>Or learn more on the website: https://inspector.dev</p>
<p><img src="https://cdn.devdojo.com/images/october2024/monitoring1.png" alt="Inspector laravel monitoring dashboard"></p>
]]></description>
                                                            <category>php</category>
                                            <category>laravel</category>
                                            <category>webdev</category>
                                            <category>tutorial</category>
                                                    <author><![CDATA[ ]]></author>
                <guid>https://devdojo.com/12460</guid>
                <pubDate>Tue, 22 Oct 2024 05:13:34 -0700</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[Environment Variables in Rust]]></title>
                <link>https://devdojo.com/francesco/environment-variables-in-rust</link>
                <description><![CDATA[<p>Environment variables are a set of key-value pairs stored in the operating system.</p>
<p>They are used to store configuration settings and other information required by the system and other applications.</p>
<p>In this article, we'll explore how to work with environment variables in Rust using the standard library and the <code>dotenv</code> crate.</p>
<h2 id="what-are-environment-variables">What Are Environment Variables?</h2>
<p>Environment variables provide a flexible way to configure your applications without hardcoding values directly in your source code.</p>
<p>This makes it easier to manage different configurations for different environments, (development, testing, and production) and to keep sensitive information, such as API keys, secure.</p>
<p>Let's see 3 different examples of how we can use the environment variables in Rust.</p>
<ol>
<li>Using the <code>std::env</code> module</li>
<li>Using the command line (Windows and Linux)</li>
<li>Using the <code>dotenv</code> crate</li>
</ol>
<p>If you prefer a video version:
<iframe width="100%" height="399" src="https://www.youtube.com/embed/5mL3mFGQHM0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
<p>All the code is available on <a href="https://youtu.be/5mL3mFGQHM0">Github</a> (link in the video description)</p>
<h2 id="toc-1-environemnt-variables-using-the-std-env-module">1. Environemnt Variables using the <code>std::env</code> module</h2>
<p>Rust provides the <code>std::env</code> module to interact with environment variables. This module can read, set, and remove environment variables.</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="importing-the-env-module">Importing the <code>env</code> Module</h3>
<p>First, import the <code>env</code> module from the standard library:</p>
<pre><code class="hljsphp"><span class="hljs-keyword">use</span> <span class="hljs-title">std</span>::<span class="hljs-title">env</span>;
</code></pre>
<p>You can test this by typing <code>cargo run</code> in the terminal.</p>
<p>Your output should be something like this:</p>
<p><a href="https://youtu.be/5mL3mFGQHM0"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2teymua943zt4fm6ei2q.png" alt="Environment variables in Rust"></a></p>
<h3 id="setting-an-environment-variable">Setting an Environment Variable</h3>
<p>You can set an environment variable using the set_var function. Here's an example where we set a variable AAA with the value 123:</p>
<pre><code class="hljsruby">fn main() {
    let key = <span class="hljs-string">"AAA"</span>;
    std::env::set_var(key, <span class="hljs-string">"123"</span>); <span class="hljs-regexp">//</span> Sets AAA to <span class="hljs-number">123</span>
}
</code></pre>
<h3 id="removing-an-environment-variable">Removing an Environment Variable</h3>
<p>To remove an environment variable, use the remove_var function:</p>
<pre><code class="hljsjavascript">fn main() {
    <span class="hljs-keyword">let</span> key = <span class="hljs-string">"AAA"</span>;
    env::remove_var(key); <span class="hljs-comment">// Removes the variable AAA</span>
}
</code></pre>
<h3 id="checking-if-an-environment-variable-exists">Checking If an Environment Variable Exists</h3>
<p>To check if an environment variable exists, use the env::var function, which returns a Result. You can handle this with a match statement:</p>
<pre><code class="hljsgo">fn main() {
    let key = <span class="hljs-string">"AAA"</span>;

    match env::<span class="hljs-keyword">var</span>(key) {
        Ok(val) =&gt; <span class="hljs-built_in">println</span>!(<span class="hljs-string">"{}: {:?}"</span>, key, val),
        Err(e) =&gt; <span class="hljs-built_in">println</span>!(<span class="hljs-string">"Error {}: {}"</span>, key, e),
    }
}
</code></pre>
<h3 id="toc-2-environment-variables-from-the-command-line">2. Environment Variables from the Command Line</h3>
<p>You can pass environment variables directly from the command line when running your Rust program:</p>
<p>Here's how you can read the CLI_ARG environment variable:</p>
<pre><code class="hljsgo">fn main() {
    let cli_arg = env::<span class="hljs-keyword">var</span>(<span class="hljs-string">"CLI_ARG"</span>);

    match cli_arg {
        Ok(val) =&gt; <span class="hljs-built_in">println</span>!(<span class="hljs-string">"CLI_ARG: {:?}"</span>, val),
        Err(e) =&gt; <span class="hljs-built_in">println</span>!(<span class="hljs-string">"Error CLI_ARG: {}"</span>, e),
    }
}
</code></pre>
<p><a href="https://youtu.be/5mL3mFGQHM0"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mi0dm8n75kk4ukcvys3z.png" alt="Environment variables in Rust"></a></p>
<p>To read them from the command line, you can use the following commands:</p>
<p>On Linux/macOS:</p>
<pre><code class="hljs">CLI_ARG=TEST cargo run
</code></pre>
<p><a href="https://youtu.be/5mL3mFGQHM0"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3h768i4yyslf09vr9v89.png" alt="Environment variables in Rust"></a></p>
<p>On Windows(powershell):</p>
<pre><code class="hljsruby">$env<span class="hljs-symbol">:CLI_ARG=<span class="hljs-string">"TEST"</span></span>; cargo run
</code></pre>
<p><a href="https://youtu.be/5mL3mFGQHM0"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5s7lzhhba9utbvynujfy.png" alt="Environment variables in Rust"></a></p>
<h2 id="toc-3-environment-variables-with-the-dotenv-crate">3. Environment Variables with the dotenv Crate</h2>
<p>In addition to the standard library, you can use the dotenv crate to load environment variables from a .env file. This is particularly useful in development environments.</p>
<p>First, add dotenv to your Cargo.toml file:</p>
<pre><code class="hljsgo">[dependencies]
dotenv = <span class="hljs-string">"0.15.0"</span>
</code></pre>
<p>And create a .env file in the root of your project with the following content:</p>
<pre><code class="hljs">API_KEY=1234567890
</code></pre>
<h3 id="loading-environment-variables-from-a-env-file">Loading Environment Variables from a .env File</h3>
<p>After adding dotenv, you can use it to load variables from a .env file:</p>
<pre><code class="hljsphp"><span class="hljs-keyword">use</span> <span class="hljs-title">dotenv</span>::<span class="hljs-title">dotenv</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">std</span>::<span class="hljs-title">env</span>;

fn main() {
    dotenv().ok(); <span class="hljs-comment">// Reads the .env file</span>

    let api_key = env::var(<span class="hljs-string">"API_KEY"</span>);

    match api_key {
        Ok(val) =&gt; println!(<span class="hljs-string">"API_KEY: {:?}"</span>, val),
        Err(e) =&gt; println!(<span class="hljs-string">"Error API_KEY: {}"</span>, e),
    }

    <span class="hljs-comment">//Simulate the execution of the rest of the program</span>
    println!(<span class="hljs-string">"...program continues..."</span>);
}
</code></pre>
<p>And you if you run the program, you should see the output:</p>
<p><a href="https://youtu.be/5mL3mFGQHM0"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vf4wel6n7sd6o4cejyiy.png" alt="Environment variables in Rust"></a></p>
<p>In the example above, the dotenv().ok(); line loads the variables from a .env file located in the root of your project. The program then attempts to read the API_KEY variable and prints its value or an error if it is not set.</p>
<h2 id="conclusion">Conclusion</h2>
<p>These are just three basic examples of working with environment variables in Rust.</p>
<p>I hope everything is clear and you can use them in your projects. If you have any questions, feel free to ask in the comments below.</p>
<p>If you prefer a video version:
<iframe width="100%" height="399" src="https://www.youtube.com/embed/5mL3mFGQHM0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
<p>All the code is available on <a href="https://youtu.be/5mL3mFGQHM0">Github</a> (link in the video description)</p>
<p>You can find me here: <a href="https://francescociulla.com">Francesco</a></p>
]]></description>
                                                                    <author><![CDATA[Francesco Ciulla]]></author>
                <guid>https://devdojo.com/12380</guid>
                <pubDate>Sat, 21 Sep 2024 22:04:02 -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 Auth Routes Tutorial]]></title>
                <link>https://devdojo.com/inspector/laravel-auth-routes-tutorial</link>
                <description><![CDATA[<p>Laravel auth routes is one of the essential features of the Laravel framework. Using middlewares you can implement different authentication strategies and attach them to different parts of your backend.</p>
<p>Laravel offers a complete and flexible authentication system, capable of adapting to various needs and implementations. There are also several external packages to implement additional authentication processes other than what ship with the framework by default.</p>
<p>In this article I show you the implications of the standard authentication system and other auth routes systems I navigated in the last ten years of experience with Laravel.</p>
<h2 id="authentication-guards-e-providers-in-laravel">Authentication Guards e Providers in Laravel</h2>
<p>The logic behind routes authentication in Laravel is based on the implementation of two different objects:</p>
<ul>
<li>Guard: defines how to determine whether an HTTP request is coming from an authenticated user;</li>
<li>Provider: defines how to retrieve registered users for the application.
You can check out these configurations in the config/auth.php configuration file.</li>
</ul>
<pre><code class="hljsphp"><span class="hljs-comment">/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
*/</span>
<span class="hljs-string">'guards'</span> =&gt; [
    <span class="hljs-string">'web'</span> =&gt; [
        <span class="hljs-string">'driver'</span> =&gt; <span class="hljs-string">'session'</span>,
        <span class="hljs-string">'provider'</span> =&gt; <span class="hljs-string">'users'</span>,
    ],

    <span class="hljs-string">'api'</span> =&gt; [
        <span class="hljs-string">'driver'</span> =&gt; <span class="hljs-string">'passport'</span>,
        <span class="hljs-string">'provider'</span> =&gt; <span class="hljs-string">'users'</span>,
    ],
],
</code></pre>
<pre><code class="hljsphp"><span class="hljs-comment">/*
|----------------------------------------------------------------------
| User Providers
|----------------------------------------------------------------------
|
*/</span>

<span class="hljs-string">'providers'</span> =&gt; [
    <span class="hljs-string">'users'</span> =&gt; [
        <span class="hljs-string">'driver'</span> =&gt; <span class="hljs-string">'eloquent'</span>,
        <span class="hljs-string">'model'</span> =&gt; \App\Domains\Organization\Models\User::class,
    ],
],
</code></pre>
<p>A newly created Laravel application provides a default guard and provider configuration suited to the needs of an application that only delivers web pages and generates the HTML of its pages entirely on the server side.</p>
<p>The default guard is “session” – which uses session cookies to establish whether the request comes from an authenticated user – and the default provider is the “database” that retrieves the registered users information via Eloquent.</p>
<p>If you are not using a traditional relational database to store your users, you can extend Laravel with your own authentication user provider. Here is an example from the official documentation:</p>
<pre><code class="hljsxml"><span class="php"><span class="hljs-meta">&lt;?php</span>
 
<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Providers</span>;
 
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Extensions</span>\<span class="hljs-title">MongoUserProvider</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Contracts</span>\<span class="hljs-title">Foundation</span>\<span class="hljs-title">Application</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Auth</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">ServiceProvider</span>;
 
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppServiceProvider</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ServiceProvider</span>
</span>{
    <span class="hljs-comment">// ...</span>
 
    <span class="hljs-comment">/**
     * Bootstrap any application services.
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">boot</span><span class="hljs-params">()</span>: <span class="hljs-title">void</span>
    </span>{
        Auth::provider(<span class="hljs-string">'mongo'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(Application $app, array $config)</span> </span>{
            <span class="hljs-comment">// Return an instance of Illuminate\Contracts\Auth\UserProvider...</span>
 
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> MongoUserProvider($app-&gt;make(<span class="hljs-string">'mongo.connection'</span>));
        });
    }
}
</span></code></pre>
<p>Than you can change the default provider in the configuration file:</p>
<pre><code class="hljsphp"><span class="hljs-string">'providers'</span> =&gt; [
    <span class="hljs-string">'users'</span> =&gt; [
        <span class="hljs-string">'driver'</span> =&gt; <span class="hljs-string">'mongo'</span>,
    ],
],
</code></pre>
<p>Finally, you must reference this new provider in the guards configuration:</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"><span class="hljs-string">'guards'</span> =&gt; [
    <span class="hljs-string">'web'</span> =&gt; [
        <span class="hljs-string">'driver'</span> =&gt; <span class="hljs-string">'session'</span>,
        <span class="hljs-string">'provider'</span> =&gt; <span class="hljs-string">'users'</span>,
    ],
],
</code></pre>
<p>There are many situations where the default configuration is not enough. For example, the case in which your application offers RESTful APIs or if it’s the backend of a Single Page Application. In this case, you need to include and configure other authentication services offered by the Laravel ecosystem.</p>
<p>For example, in the case of the API server, you should opt for a token-type guard, which allows you to recognize clients authenticated by a specific token present in the request. Personally I use a Laravel first-party package called <a href="https://laravel.com/docs/master/passport">Passport</a> that implements a complete OAuth server so I can manage authentication for frontend and backend API with the same framework and a very granular logic.</p>
<h2 id="user-eloquent-model-as-authentication-provider">User eloquent model as authentication provider</h2>
<p>It's not a coincidence that the <code>App\Models\User</code> class does not directly extend the basic Eloquent Model class, but the <code>Illuminate\Foundation\Auth\User</code> class. This allows us to identify it as an appropriate model to be a provider for the users of the application.</p>
<pre><code class="hljsphp"><span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Foundation</span>\<span class="hljs-title">Auth</span>\<span class="hljs-title">User</span> <span class="hljs-title">as</span> <span class="hljs-title">Authenticatable</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Authenticatable</span>
</span>{
    <span class="hljs-keyword">protected</span> $fillable = [
        <span class="hljs-string">'name'</span>,
        <span class="hljs-string">'email'</span>,
        <span class="hljs-string">'password'</span>,
    ];

    <span class="hljs-keyword">protected</span> $hidden = [
        <span class="hljs-string">'password'</span>,
        <span class="hljs-string">'remember_token'</span>,
    ];
}
</code></pre>
<h2 id="check-if-a-session-is-authenticated">Check if a session is authenticated</h2>
<p>HTTP requests coming from a client that has successfully completed authentication can be spotted at various points in the stack. This allows you to intervene in appropriate ways to prevent certain actions or modify the application behaviors accordingly.</p>
<p>You can perform this check in controllers, views and even routes. Both individual endpoints and grouped routes.</p>
<p>The best way to enforce mandatory authentication on endpoints is using middleware. Laravel provides two built-in middlewares:</p>
<ul>
<li>guest – for routes accessible from unauthenticated clients;</li>
<li>auth – for routes accessible only by authenticated users;</li>
</ul>
<p>These middlewares are available as "route-specific" middlewares and can therefore be applied to each route, independently of any other middleware.</p>
<p>Navigate to the <code>App\Http\Kernel</code> class or in the <code>bootstrap/app.php</code> file inside the <code>withMiddleware()</code> method.</p>
<pre><code class="hljsphp"><span class="hljs-comment">/**
* The application's route middleware.
*
* These middlewares may be assigned to groups or used individually.
*
* <span class="hljs-doctag">@var</span> array
*/</span>
<span class="hljs-keyword">protected</span> $middlewareAliases = [
    <span class="hljs-string">'auth'</span> =&gt; \App\Http\Middleware\Authenticate::class,
    <span class="hljs-string">'guest'</span> =&gt; \App\Http\Middleware\RedirectIfAuthenticated::class,
	
    ...
];
</code></pre>
<p>You can use them to control access to your routes:</p>
<pre><code class="hljsgo"><span class="hljs-comment">// The home page is accessible to everyone</span>
Route::get(<span class="hljs-string">'/'</span>, [WebsiteController:class, <span class="hljs-string">'home'</span>]);

<span class="hljs-comment">// Register routes are accessible only to unauthenticated users</span>
Route::get(<span class="hljs-string">'/register'</span>, [RegisterController::class, <span class="hljs-string">'create'</span>])
	-&gt;name(<span class="hljs-string">'register'</span>)
	-&gt;middleware(<span class="hljs-string">'guest'</span>);

Route::post(<span class="hljs-string">'/register'</span>, [RegisterController::class, <span class="hljs-string">'store'</span>])
	-&gt;middleware(<span class="hljs-string">'guest'</span>);

<span class="hljs-comment">// Dashboard is accessible only to authenticated users</span>
Route::get(<span class="hljs-string">'/dashboard'</span>, [DashboardController::class, <span class="hljs-string">'home'</span>])
	-&gt;middleware(<span class="hljs-string">'auth'</span>);
</code></pre>
<h2 id="the-difference-between-guest-and-auth-route-middlewares">The difference between GUEST and AUTH route middlewares</h2>
<p>The "guest" middleware is used to restrict access to certain routes or actions to unauthenticated users only.</p>
<p>Restrict access to unauthenticated users?</p>
<p>Yes, in fact as you can see in the snippet above the class associated with the guest middleware is "RedirectIfAuthenticated".</p>
<p>So guest allows you to make the routes accessible if you are a “free” user, but the moment you authenticate to the website Laravel will redirect you to the "private" section of the application. You can’t navigate "guest routes" if you are authenticated.</p>
<p>In a nutshell, "guest" is useful for the registration page, not in an e-commerce product page :).</p>
<p>The purpose of the auth middleware, however, is not at all ambiguous. It checks if the current Request is authenticated. Otherwise you are redirected to the public section of the application, such as the login page.</p>
<h2 id="authentication-in-laravel-blade-template">Authentication In Laravel Blade Template</h2>
<p>At this point, you may want to change the behavior of some of your application pages to show different content depending on whether it is a visitor or an authenticated user. To do this, we can use the @auth directive:</p>
<pre><code class="hljsxml">@auth
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Welcome {{ auth()-&gt;user()-&gt;name }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
@else
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ route('login') }}"</span>&gt;</span>Login<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">"{{ route('register') }}"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
@endauth
</code></pre>
<p>The @auth and @guest directives operate like an if statement, thus allowing portions of the view to be rendered if the rendering of the view is requested by an authenticated user or a visitor respectively.</p>
<h2 id="how-to-get-the-authenticated-user">How to get the authenticated user</h2>
<p>Laravel provides you with a built-in service called "Auth" that allows you to operate transparently with the underlying user providers and guards.To access the Auth service you can use the Facade <code>Illuminate\Support\Facades\Auth</code>, or the helper function auth().</p>
<pre><code class="hljsphp"><span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Auth</span>;

<span class="hljs-comment">// Access the authenticated User instance</span>
$name = Auth::user()-&gt;name;
$name = auth()-&gt;user()-&gt;name;

<span class="hljs-comment">// Check if the current session is authenticated</span>
<span class="hljs-keyword">if</span> (Auth::check()) {
	<span class="hljs-comment">// User logged in</span>
}
</code></pre>
<h2 id="logout-terminate-an-authenticated-session-in-laravel">Logout: Terminate an authenticated session in Laravel</h2>
<p>In your <code>routes/web.php</code> you should have the logout route:</p>
<pre><code class="hljsphp">Route::get(<span class="hljs-string">'logout'</span>, [LoginController::class, <span class="hljs-string">'logout'</span>)-&gt;middleware(<span class="hljs-string">'auth'</span>);
</code></pre>
<p>In the LoginController.php</p>
<pre><code class="hljsphp"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">logout</span><span class="hljs-params">(Request $request)</span> 
</span>{
    Auth::logout();
    <span class="hljs-keyword">return</span> redirect(<span class="hljs-string">'/login'</span>);
}
</code></pre>
<p>Now, you are able to logout using <code>yourdomain.com/logout</code> URL or if you have created a logout button, add href to "/logout".</p>
<p>The logout method will clear the authentication information in the user’s session.</p>
<h2 id="logout-other-devices">Logout other devices</h2>
<p>Invalidating sessions on other devices Laravel also provides a mechanism for invalidating and "logging out" user sessions that are active on other devices without invalidating the session on their current device. This feature is typically used when a user changes or updates their password and you want to invalidate sessions on other devices while maintaining the authenticity of the current device.</p>
<p>To implement this feature Laravel provides a built-in middleware that you should add to the “web” middleware group: <code>\Illuminate\Session\Middleware\AuthenticateSession</code></p>
<pre><code class="hljsphp"><span class="hljs-string">'web'</span> =&gt; [
    <span class="hljs-comment">// ...</span>
    \Illuminate\Session\Middleware\AuthenticateSession::class,
    <span class="hljs-comment">// ...</span>
],
</code></pre>
<p>Once the middleware is attached you can use the logoutOtherDevices() method on the Auth service.</p>
<pre><code class="hljsphp"><span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Auth</span>;

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">logoutOtherDevices</span><span class="hljs-params">(Request $request)</span>
</span>{
    $password = $request-&gt;input(<span class="hljs-string">'password'</span>);

    <span class="hljs-keyword">if</span> (Hash::check($password, Auth::user()-&gt;password)) {
        Auth::logoutOtherDevices($password);
        
        <span class="hljs-comment">// Optionally, you can regenerate the user's session ID</span>
        $request-&gt;session()-&gt;regenerate();
        
        <span class="hljs-keyword">return</span> redirect()-&gt;back()-&gt;with(<span class="hljs-string">'success'</span>, <span class="hljs-string">'Logged out from other devices successfully.'</span>);
    }

    <span class="hljs-keyword">return</span> redirect()-&gt;back()-&gt;withErrors([<span class="hljs-string">'password'</span> =&gt; <span class="hljs-string">'Invalid password.'</span>]);
}
</code></pre>
<p>After the <code>logoutOtherDevices</code> method is executed, the user remains logged in on the current device, but all other sessions associated with that user are terminated. When the user tries to access the application from other devices, they will be required to log in again.After logging out from other devices, you can optionally regenerate the user's session ID using <code>$request-&gt;session()-&gt;regenerate()</code> to further enhance security.</p>
<p>You can follow me on <a href="https://www.linkedin.com/in/valeriobarbera/">Linkedin</a> or <a href="https://x.com/Barbalerio">X</a>. I post about building my SaaS product.</p>
<h2 id="monitor-your-php-application-for-free">Monitor your PHP application for free</h2>
<p>Inspector is a Code Execution Monitoring tool specifically designed for software developers. You don't need to install anything at the server level, just install the <strong><a href="https://github.com/inspector-apm">composer package</a></strong> and you are ready to go.</p>
<p>Inspector is super easy and PHP friendly. You can try our <a href="https://github.com/inspector-apm/inspector-laravel">Laravel</a> or <a href="https://github.com/inspector-apm/inspector-symfony">Symfony</a> package.</p>
<p>If you are looking for HTTP monitoring, database query insights, and the ability to forward alerts and notifications into your preferred messaging environment, try Inspector for free. <a href="https://app.inspector.dev/register">Register your account</a>.</p>
<p>Or learn more on the website: https://inspector.dev</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k6357b2uayubrincq9nh.png" alt="Laravel application monitoring"></p>
]]></description>
                                                            <category>php</category>
                                            <category>laravel</category>
                                            <category>webdev</category>
                                                    <author><![CDATA[ ]]></author>
                <guid>https://devdojo.com/12366</guid>
                <pubDate>Sat, 14 Sep 2024 05:59:22 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Laravel Livewire: What it is, and how to use it in your web app]]></title>
                <link>https://devdojo.com/inspector/laravel-livewire-what-it-is-and-how-to-use-it-in-your-web-app</link>
                <description><![CDATA[<p>Livewire is one of the most important projects in the Laravel ecosystem specifically targeted to frontend development. Livewire v3 has been recently released, so let’s explore what Livewire is, and what kind of projects fits its architecture.</p>
<p>The peculiarity of Livewire is that it allows the development of a "modern" web application without the need to use dedicated JavaScript frameworks.</p>
<p>With Livewire it is possible to develop Blade components that offer a level of reactivity equal to that offered by Vue or React, without the need to manage the complexity of a project with a decoupled frontend and backend.  You can continue developing your application within the borders of Laravel and Blade templates.</p>
<h2 id="how-livewire-works">How Livewire Works</h2>
<p><a href="https://laravel-livewire.com/">Livewire</a> is a Composer package that you can add to a Laravel project. It must then be activated on each HTML page (or the page, in case you want to create a Single Page Application) using appropriate Blade directives. Livewire components consist of a PHP class and a Blade file that contains the logic of how a specific frontend component works and it must be rendered.</p>
<p>When the browser asks to access a page where Livewire is used, the following happens:</p>
<ul>
<li>The page is rendered with the initial states of the component, like any page created using Blade;</li>
<li>When the component UI fires an interaction an AJAX call is made to an appropriate route indicating the Livewire component and the interaction that occurred, plus the status of the component;</li>
<li>The data is processed in the PHP part of the component, which performs the new rendering as a result of the interaction and sends it back to the browser;</li>
<li>The DOM of the page is changed according to the changes received from the server.</li>
</ul>
<p>It's very similar to what Vue and React do, but in this case the reactivity logic to respond to an interaction is managed by the backend and not in the javascript side.</p>
<p>To help you better understand the logic I’ll show you an example of this comparison below.</p>
<p>If you want to learn more about the challenges of building a developers driven company, you can follow me on <a href="https://www.linkedin.com/in/valeriobarbera/">Linkedin</a> or <a href="https://x.com/Barbalerio">X</a>.</p>
<h2 id="how-to-install-laravel-livewire">How to install Laravel Livewire</h2>
<p>Livewire installation is absolutely minimal. Install the Composer package in your Laravel project and add the necessary Blade directives to all pages (or to the common layout from which all Blade templates in the project are derived).</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">composer <span class="hljs-built_in">require</span> livewire/livewire
</code></pre>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    ...

    @livewireStyles
<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>
    ...

    @livewireScripts
<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>
<h2 id="how-to-create-a-laravel-livewire-component">How to create a Laravel Livewire Component</h2>
<p>Once the Composer package is installed, a new Artisan make sub-command is available to create a new Livewire component. Each component will be made with a PHP class and a Blade view.</p>
<p>It's similar to the <a href="https://laravel.com/docs/master/blade#components">class-based components of Blade</a>.</p>
<pre><code class="hljsgo">php artisan <span class="hljs-built_in">make</span>:livewire SpyInput    
COMPONENT CREATED 🤙

CLASS: app/Http/Livewire/SpyInput.php
VIEW: resources/views/livewire/spy-input.blade.php
</code></pre>
<p>The component in this example will "spy" what is written in an HTML input field, without the need to write JavaScript code.</p>
<p>We then insert a public property to the component class:</p>
<pre><code class="hljsphp"><span class="hljs-comment">// app/Http/Livewire/SpyInput.php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Livewire</span>;
 
<span class="hljs-keyword">use</span> <span class="hljs-title">Livewire</span>\<span class="hljs-title">Component</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SpyInput</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Component</span>
</span>{
    <span class="hljs-keyword">public</span> string $message;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">render</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-keyword">return</span> view(<span class="hljs-string">'livewire.spy-input'</span>);
    }
}
</code></pre>
<p>Implement the component view as follows:</p>
<pre><code class="hljsxml">// resources/views/livewire/spy-input.blade.php

<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>Type here:<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">"text"</span> <span class="hljs-attr">wire:model</span>=<span class="hljs-string">"message"</span>/&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>You typed: <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{{ $message }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>And finally put the Livewire component in a blade view:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    @livewireStyles
<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">livewire:spy-input</span> /&gt;</span>

    @livewireScripts
<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>In a normal Blade component all public properties of the component class are visible in the blade template. So in <code>&lt;span&gt;{{ $message }}&lt;/span&gt;</code> the value of the <strong>$message</strong> property will be automatically displayed. In a normal class based component, however, this only happens on the first component rendering. If you type something in the input field nothing changes in the span tag.</p>
<p>In the Livewire component, however, we used the <code>wire:model="message"</code> attribute in the <input> field. This attribute ensures that the value of the input field is linked to the $message property in the PHP class. When you write the new value in the input field it is sent to the server, which updates the value of $message and performs a new render, sending it back to the frontend which, then, updates the text in <code>&lt;span&gt;{{ $message }}&lt;/span&gt;</code>.</p>
<p>By opening the Network tab of the browser's development tools, we will notice that on each key press on the keyboard makes a call to the server on the route below:</p>
<pre><code class="hljsxml">/livewire/message/<span class="hljs-tag">&lt;<span class="hljs-name">COMPONENT-NAME</span>&gt;</span>
</code></pre>
<p>The response to each call contains the new rendered HTML for the component, which Livewire will insert into the page in place of the old one. Various custom wire attributes are available. For example you can execute a public method of the component class when clicking on a button. Here is an example of this biding:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">wire:click</span>=<span class="hljs-string">"doSomething"</span>&gt;</span>Click Here<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<pre><code class="hljsphp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SpyInput</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Component</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doSomething</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-comment">// Your code here…</span>
    }
}
</code></pre>
<p>where <em>doSomething</em> is a public method of the PHP class of the Livewire component.</p>
<h2 id="integration-with-other-laravel-features">Integration with other Laravel features</h2>
<p>The PHP class connected to the component behaves like any other PHP class in a Laravel project. The only difference is that it uses the mount method instead of the classic <code>__construct</code> class constructor to initialize the public properties of the class.</p>
<pre><code class="hljsruby">{{-- Initial assignment of the the $book property <span class="hljs-keyword">in</span> the ShowBook <span class="hljs-class"><span class="hljs-keyword">class</span> --}}</span>
&lt;<span class="hljs-symbol">livewire:</span>show-book <span class="hljs-symbol">:book=<span class="hljs-string">"$book"</span>&gt;</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ShowBook</span> <span class="hljs-title">extends</span> <span class="hljs-title">Component</span></span>
{
    public $title;
    public $excerpt;

    <span class="hljs-regexp">//</span> <span class="hljs-string">"mount"</span> instead of <span class="hljs-string">"__constuct"</span>
    public function mount(Book $book = null)
    {
        $this-&gt;title = $book-&gt;title;
        $this-&gt;excerpt = $book-&gt;excerpt;
    }
}
</code></pre>
<p>You can also use the protected property <code>$rules</code> to configure the validation restrictions on the data sent from the frontend to the backend. You have to call the <code>validate()</code> method to validate the data:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">wire:submit.prevent</span>=<span class="hljs-string">"saveBook"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">wire:model</span>=<span class="hljs-string">"title"</span>/&gt;</span>
    @error('title') <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"error"</span>&gt;</span>{{ $message }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span> @enderror

    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">wire:model</span>=<span class="hljs-string">"excerpt"</span>/&gt;</span>
    @error('excerpt') <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"error"</span>&gt;</span>{{ $message }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span> @enderror

    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">wire:model</span>=<span class="hljs-string">"isbn"</span>/&gt;</span>
    @error('isbn') <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"error"</span>&gt;</span>{{ $message }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span> @enderror

    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Save Book<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>
</code></pre>
<pre><code class="hljsphp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BookForm</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Component</span>
</span>{
    <span class="hljs-keyword">public</span> $title;
    <span class="hljs-keyword">public</span> $excerpt;
    <span class="hljs-keyword">public</span> $isbn;

    <span class="hljs-keyword">protected</span> $rules = [
        <span class="hljs-string">'title'</span> =&gt; [<span class="hljs-string">'required'</span>, <span class="hljs-string">'max:200'</span>],
        <span class="hljs-string">'isbn'</span> =&gt; [<span class="hljs-string">'required'</span>, <span class="hljs-string">'unique:books'</span>, <span class="hljs-string">'size:17'</span>],
        <span class="hljs-string">'excerpt'</span> =&gt; <span class="hljs-string">'max:500'</span>
    ];

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">saveBook</span><span class="hljs-params">()</span>
    </span>{
        $validated = <span class="hljs-keyword">$this</span>-&gt;validate(<span class="hljs-keyword">$this</span>-&gt;rules);

        Book::create($validated);

        <span class="hljs-keyword">return</span> redirect()-&gt;to(<span class="hljs-string">'/books);
    }
}
</span></code></pre>
<p>Or you can use PHP Attributes to declare the desired validation rules for a class property:</p>
<pre><code class="hljsphp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BookForm</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Component</span>
</span>{
    <span class="hljs-comment">#[Validate('required|max:200')]</span>
    <span class="hljs-keyword">public</span> $title;

    <span class="hljs-comment">#[Validate('required|unique:books|size:17')]</span>
    <span class="hljs-keyword">public</span> $isbn;

    <span class="hljs-comment">#[Validate('max:500')]</span>
    <span class="hljs-keyword">public</span> $excerpt;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">saveBook</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;validate();

        Book::create([
            <span class="hljs-string">'title'</span> =&gt; <span class="hljs-keyword">$this</span>-&gt;title,
            <span class="hljs-string">'isbn'</span> =&gt; <span class="hljs-keyword">$this</span>-&gt;isbn,
            <span class="hljs-string">'excerpt'</span> =&gt; <span class="hljs-keyword">$this</span>-&gt;excerpt,
        ]);

        <span class="hljs-keyword">return</span> redirect()-&gt;to(<span class="hljs-string">'/books);
    }
}
</span></code></pre>
<p>In general, each Livewire component behaves in the ways that a Laravel developer expects from a PHP class inside a Laravel project. Thus allowing the creation of reactive web interfaces without the need to separate the development projects between Laravel and Vue/React.</p>
<h2 id="monitor-your-laravel-application-for-free">Monitor your Laravel application for free</h2>
<p>Inspector is a Code Execution Monitoring tool specifically designed for software developers. You don't need to install anything at the server level, just install the <strong><a href="https://github.com/inspector-apm/inspector-laravel">Laravel package</a></strong> and you are ready to go.</p>
<p>If you are looking for HTTP monitoring, database query insights, and the ability to forward alerts and notifications into your preferred messaging environment, try Inspector for free. <a href="https://app.inspector.dev/register">Register your account</a>.</p>
<p>Or learn more on the website: https://inspector.dev</p>
<p><img src="https://cdn.devdojo.com/images/september2024/segments-statistics2.png" alt="Laravel Code Execution Monitoring"></p>
]]></description>
                                                            <category>php</category>
                                            <category>laravel</category>
                                            <category>livewire</category>
                                            <category>webdev</category>
                                                    <author><![CDATA[ ]]></author>
                <guid>https://devdojo.com/12362</guid>
                <pubDate>Wed, 11 Sep 2024 07:39:17 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Laravel Password Hashing With Salt]]></title>
                <link>https://devdojo.com/inspector/laravel-password-hashing-with-salt</link>
                <description><![CDATA[<p>Recently we went deep into the Laravel authentication system for some improvements and to add Multi-Factor authentication. I discovered some interesting details on how Laravel password hashing works.</p>
<p>This article can help you understand how secure your application is from this point of view or if it is necessary to make some changes in your PHP app to increase security.</p>
<p>Spoiler: Despite the title of the article, Laravel doesn't use a salt to hash your user's password, PHP does.</p>
<p>You can follow me on <a href="https://www.linkedin.com/in/valeriobarbera/">Linkedin</a> or <a href="https://x.com/Barbalerio">X</a>. I post about building my SaaS product.</p>
<h2 id="hashing-is-different-from-encryption">Hashing is different from Encryption</h2>
<p>To avoid any misunderstanding, let me clarify that Hashing and Encryption are two very different things. Hashing is one way. Starting from a hash you cannot get back to the original string. Encryption instead is two ways. You can encrypt and decrypt strings.In fact these two features are provided through two different Laravel Facades: <a href="https://laravel.com/api/11.x/Illuminate/Support/Facades/Hash.html">Illuminate\Support\Facades\Hash</a> (for hashing), <a href="https://laravel.com/api/11.x/Illuminate/Support/Facades/Crypt.html">Illuminate\Support\Facades\Crypt</a> (for encryption).</p>
<p><img src="https://cdn.devdojo.com/images/september2024/password-hashing-vs-encryption-inspector.png" alt="Laravel password hashing and encryption"></p>
<p>In the case of passwords, we want to store the hash so even in case of a data breach there is no way to get back the original password so the user credentials still protected.</p>
<h2 id="how-does-laravel-make-the-password-hash">How does Laravel make the password hash?</h2>
<p>Obviously with the make method on the Hash service:</p>
<pre><code class="hljsruby">\Illuminate\Support\Facades\Hash::make(‘password’);

<span class="hljs-regexp">//</span> Output
$2y$10$PeZ29z5axgJUZiy01tMBMuQuego6WLCDUV34LJbVowg4AKcZFl4mC
</code></pre>
<p>If you run this instruction multiple times you will get a different result each time:</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">$2<span class="hljs-keyword">y</span>$1<span class="hljs-number">0</span>$PeZ29z5axgJUZiy01tMBMuQuego6WLCDUV34LJbVowg4AKcZFl4mC
$2<span class="hljs-keyword">y</span>$1<span class="hljs-number">0</span>$cYoHztp3QwzRvdTmzEE5xeXfXjbc6Ix3C9LhBungA/DIHcBLtgAE2
$2<span class="hljs-keyword">y</span>$1<span class="hljs-number">0</span>$Mz30EAiaHtZZ1J5m6yrVbuHJcZr4r4iV.RYGX8I.pZ9tT2wThGPKW
</code></pre>
<p>So, How the hell does Laravel compare two passwords to check if they are the same? It's the check we need to do when a user is logging in.</p>
<p>The counter part of the make() method is check():</p>
<pre><code class="hljsphp"><span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Hash</span>;

<span class="hljs-comment">// Stored hashed password retrieved from the database</span>
$storedHash = <span class="hljs-string">'$2y$10$Oi4mNb5kKDWtVzbs6fLCie.Uy1eG1n0BnDO1QazrF8b1/LJZVWJSO'</span>;

<span class="hljs-comment">// User-provided password during login attempt</span>
$userInputPassword = <span class="hljs-string">'password'</span>;

<span class="hljs-comment">// Verify if the user-provided password matches the stored hash</span>
<span class="hljs-keyword">if</span> (Hash::check($userInputPassword, $storedHash)) {
    <span class="hljs-keyword">echo</span> <span class="hljs-string">"Password verified!"</span>;
} <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">echo</span> <span class="hljs-string">"Invalid password!"</span>;
}
</code></pre>
<p>Laravel Password Hashing is a wrapper of PHP password functions
The Laravel password hashing component is an abstraction to use two native PHP functions with a predefined setup: <code>password_hash</code>, and <code>password_verify</code>. Both are a native wrappers of the low level <a href="https://www.php.net/manual/en/function.crypt.php">crypt</a> function.</p>
<p>This are the internal implementation of the two methods in the Laravel <code>BcryptHasher</code>:</p>
<pre><code class="hljsphp">Hash::make($password);

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">make</span><span class="hljs-params">($value, array $options = [])</span>
</span>{
    $hash = password_hash($value, PASSWORD_BCRYPT, [
        <span class="hljs-string">'cost'</span> =&gt; <span class="hljs-keyword">$this</span>-&gt;cost($options),
    ]);

    <span class="hljs-keyword">if</span> ($hash === <span class="hljs-keyword">false</span>) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(<span class="hljs-string">'Bcrypt hashing not supported.'</span>);
    }

    <span class="hljs-keyword">return</span> $hash;
}
</code></pre>
<pre><code class="hljsphp">Hash::check($password, $storedHash);

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">check</span><span class="hljs-params">($value, $hashedValue, array $options = [])</span>
</span>{
    <span class="hljs-keyword">if</span> (is_null($hashedValue) || strlen($hashedValue) === <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    }

    <span class="hljs-keyword">return</span> password_verify($value, $hashedValue);
}
</code></pre>
<p>As mentioned in the PHP documentation:</p>
<blockquote>
<p>password_hash() uses a strong hash, generates a strong salt, and applies proper rounds automatically. password_hash() is a simple crypt() wrapper and compatible with existing password hashes. Use of password_hash() is encouraged.</p>
</blockquote>
<p>In the examples above I reported some results of the <code>Hash::make()</code> that are the result of the <code>password_hash()</code> function eventually. You can notice the same pattern at the beginning of the string. It's a composition of the algorithm, cost and salt as part of the returned hash. Therefore, all information that's needed to verify the hash is included in it. This allows the <code>password_verify()</code> function to verify the hash without needing separate storage for the salt or algorithm information.</p>
<p>Internally, the <code>password_verify()</code> function works by performing the following steps:</p>
<p><strong>Extracting Parameters</strong>: It extracts the parameters stored within the hash string itself. These parameters include the hashing algorithm, the cost, and the salt.</p>
<p><strong>Hashing the Input Password</strong>: Using the extracted parameters, <code>password_verify()</code> rehashes the user-provided password using the same algorithm, salt, and cost factor that were used to generate the stored hash.</p>
<p><strong>Comparison</strong>: After rehashing the input password, it compares the resulting hash with the stored hash. If the two hashes match, the input password is correct.</p>
<p>Here is an example of how the <code>password_verify()</code> function can be implemented in plain PHP so you can understand the process:</p>
<pre><code class="hljsphp"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">password_verify</span><span class="hljs-params">($userInputPassword, $storedHash)</span> </span>{
    <span class="hljs-comment">// Extract parameters from the stored hash</span>
    $params = explode(<span class="hljs-string">'$'</span>, $storedHash);
    $algorithm = $params[<span class="hljs-number">1</span>];
    $cost = (int)$params[<span class="hljs-number">2</span>];
    $salt = $params[<span class="hljs-number">3</span>];

    <span class="hljs-comment">// Rehash the user input password using the extracted parameters</span>
    $rehashedPassword = crypt($userInputPassword, <span class="hljs-string">'$'</span> . $algorithm . <span class="hljs-string">'$'</span> . $cost . <span class="hljs-string">'$'</span> . $salt);

    <span class="hljs-comment">// Compare the rehashed password with the stored hash</span>
    <span class="hljs-keyword">if</span> ($rehashedPassword === $storedHash) {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    }
}
</code></pre>
<h2 id="security-considerations">Security considerations</h2>
<p><code>password_hash()</code> already generates a strong hash with a salt. From the Laravel configuration you can eventually change the cost factor through the <code>bcrypt.rounds</code> parameter in the <code>config/hashing.php</code> file.</p>
<pre><code class="hljsphp"><span class="hljs-comment">/*
|--------------------------------------------------------------------------
| Bcrypt Options
|--------------------------------------------------------------------------
|
| Here you may specify the configuration options that should be used when
| passwords are hashed using the Bcrypt algorithm. This will allow you
| to control the amount of time it takes to hash the given password.
|
*/</span>

<span class="hljs-string">'bcrypt'</span> =&gt; [
	<span class="hljs-string">'rounds'</span> =&gt; env(<span class="hljs-string">'BCRYPT_ROUNDS'</span>, <span class="hljs-number">10</span>),
],
</code></pre>
<p>Increasing the cost factor (number of rounds) in the Bcrypt algorithm generally improves the security of passwords by making password hashing more computationally intensive and resistant to brute-force attacks.</p>
<p>For example the default value of the rounds parameter is 10, I use 12 in my application to strengthen my passwords a bit.</p>
<h2 id="monitor-your-php-application-for-free">Monitor your PHP application for free</h2>
<p>Inspector is a Code Execution Monitoring tool specifically designed for software developers. You don't need to install anything at the server level, just install the <strong><a href="https://github.com/inspector-apm">composer package</a></strong> and you are ready to go.</p>
<p>Inspector is super easy and PHP friendly. You can try our <a href="https://github.com/inspector-apm/inspector-laravel">Laravel</a> or <a href="https://github.com/inspector-apm/inspector-symfony">Symfony</a> package.</p>
<p>If you are looking for HTTP monitoring, database query insights, and the ability to forward alerts and notifications into your preferred messaging environment, try Inspector for free. <a href="https://app.inspector.dev/register">Register your account</a>.</p>
<p>Or learn more on the website: https://inspector.dev</p>
<p><img src="https://cdn.devdojo.com/images/september2024/segments-statistics1.png" alt="PHP Code Execution Monitoring"></p>
]]></description>
                                                            <category>php</category>
                                            <category>laravel</category>
                                            <category>webdev</category>
                                                    <author><![CDATA[ ]]></author>
                <guid>https://devdojo.com/12357</guid>
                <pubDate>Mon, 09 Sep 2024 00:41:47 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Upgrade Your Node.js Testing: Seamlessly Switch from Jest to Vitest]]></title>
                <link>https://devdojo.com/keploy/upgrade-your-nodejs-testing-seamlessly-switch-from-jest-to-vitest</link>
                <description><![CDATA[<p>Jest and Vitest are two well-known JavaScript testing frameworks, each with its own strengths. Jest, created by Facebook, is especially popular for React applications. It provides a "zero-config" setup, built-in code coverage reporting, and snapshot testing, making it a complete solution for many JavaScript projects.</p>
<p>Vitest, a newer framework in the Vite ecosystem, is known for its speed and lightweight design. Using Vite's dev server, Vitest performs very well, especially in larger projects. It works with most Jest APIs, making it easier for teams already using Jest to switch.</p>
<p>While Jest has a bigger community and more third-party tools, Vitest offers native TypeScript support and better integration with ES Modules. Both frameworks can test front-end and back-end code, but Vitest works especially well with Vite-based projects, giving it an advantage in that area.</p>
<h2 id="setting-the-stage">Setting the Stage</h2>
<p>Our application is a simple Express.js API that shows basic CRUD operations. We have an <code>app.js</code> file that sets up our Express app and imports routes from a <code>routes.js</code> file. This keeps our code organized and easy to maintain. The <code>routes.js</code> file has three endpoints: a root endpoint that returns a welcome message, a GET endpoint to get a list of users, and a POST endpoint to create a new user.</p>
<pre><code class="hljsgo">app/
├── app.js
├── routes.js
├── server.js
├── test/
│   └── routes.test.js
├── <span class="hljs-keyword">package</span>.json
</code></pre>
<p>Our API is simple on purpose so we can focus on testing. The GET <code>/users</code> endpoint returns a hardcoded list of users. The POST <code>/users</code> endpoint simulates creating a user by accepting a name in the request body and returning a new user object with a generated ID. This simplicity helps us focus on how our tests are structured and how they interact with our Express app.</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">const</span> request = <span class="hljs-built_in">require</span>(<span class="hljs-string">'supertest'</span>);
<span class="hljs-keyword">const</span> app = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../app'</span>);

describe(<span class="hljs-string">'API Routes'</span>, () =&gt; {
    test(<span class="hljs-string">'GET / should return welcome message'</span>, <span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> request(app).get(<span class="hljs-string">'/'</span>);
        expect(res.statusCode).toBe(<span class="hljs-number">200</span>);
        expect(res.body).toHaveProperty(<span class="hljs-string">'message'</span>, <span class="hljs-string">'Welcome to the Express Jest Demo API!'</span>);
    });

    test(<span class="hljs-string">'GET /users should return list of users'</span>, <span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> request(app).get(<span class="hljs-string">'/users'</span>);
        expect(res.statusCode).toBe(<span class="hljs-number">200</span>);
        expect(res.body).toBeInstanceOf(<span class="hljs-built_in">Array</span>);
        expect(res.body).toHaveLength(<span class="hljs-number">2</span>);
        expect(res.body[<span class="hljs-number">0</span>]).toHaveProperty(<span class="hljs-string">'id'</span>);
        expect(res.body[<span class="hljs-number">0</span>]).toHaveProperty(<span class="hljs-string">'name'</span>);
    });

    test(<span class="hljs-string">'POST /users should create a new user'</span>, <span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> request(app)
            .post(<span class="hljs-string">'/users'</span>)
            .send({ <span class="hljs-attr">name</span>: <span class="hljs-string">'Test User'</span> });
        expect(res.statusCode).toBe(<span class="hljs-number">201</span>);
        expect(res.body).toHaveProperty(<span class="hljs-string">'id'</span>);
        expect(res.body).toHaveProperty(<span class="hljs-string">'name'</span>, <span class="hljs-string">'Test User'</span>);
    });

    test(<span class="hljs-string">'POST /users should return 400 if name is missing'</span>, <span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> request(app)
            .post(<span class="hljs-string">'/users'</span>)
            .send({});
        expect(res.statusCode).toBe(<span class="hljs-number">400</span>);
        expect(res.body).toHaveProperty(<span class="hljs-string">'error'</span>, <span class="hljs-string">'Name is required'</span>);
    });
});
</code></pre>
<p>To ensure our API functions correctly, we've implemented a suite of Jest test cases. These tests are located in the <code>tests/routes.test.js</code> file and use the <a href="https://www.npmjs.com/package/supertest">Supertest</a> library to make HTTP requests to our Express app. Let's break down the test cases:</p>
<ol>
<li>
<p><strong>Welcome Message Test</strong>: This test verifies that the root endpoint (<code>GET /</code>) returns the expected welcome message with a 200 status code.</p>
</li>
<li>
<p><strong>User List Retrieval Test</strong>: Here, we check if the <code>GET /users</code> endpoint returns an array of users with the correct structure and a 200 status code.</p>
</li>
<li>
<p><strong>User Creation Test</strong>: This test ensures that the <code>POST /users</code> endpoint successfully creates a new user when provided with a valid name, returning the created user object with a 201 status 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>
</li>
<li>
<p><strong>User Creation Error Handling Test</strong>: We also test the error case where a name is not provided in the request body, expecting a 400 status code and an appropriate error message.</p>
</li>
</ol>
<p>These test cases check the basic functions of our API to make sure it works correctly in different situations. By using Jest and Supertest, we can simulate HTTP requests and verify the responses, which helps us trust our API's behavior without doing manual tests.</p>
<h2 id="step-by-step-migration-process">Step-by-Step Migration Process</h2>
<h3 id="installing-vitest">Installing Vitest</h3>
<p>The first step in our migration process is to update our project dependencies. We'll need to remove Jest and install Vitest along with some related packages. Here's how we can do that:</p>
<ol>
<li>
<p><strong>Removing Jest dependencies</strong> Let's start by removing Jest and any Jest-related packages from our project. Open your terminal and run the following command:</p>
<pre><code class="hljs">npm uninstall jest
</code></pre>
</li>
<li>
<p><strong>Adding Vitest and related packages</strong> Now that we've removed Jest, let's install Vitest and the necessary related packages:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">npm</span> <span class="hljs-selector-tag">install</span> <span class="hljs-selector-tag">--save-dev</span> <span class="hljs-selector-tag">vitest</span> <span class="hljs-selector-tag">supertest</span> <span class="hljs-keyword">@vitest</span>/coverage-v8
</code></pre>
</li>
</ol>
<h3 id="configuring-vitest">Configuring Vitest</h3>
<p>While Vitest works out of the box for many projects, we'll create a configuration file to ensure smooth integration with our Express.js app. Create a new file named <code>vitest.config.js</code> in your project root with the following content:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'vitest/config'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  <span class="hljs-attr">test</span>: {
    <span class="hljs-attr">globals</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">environment</span>: <span class="hljs-string">'node'</span>,
  },
})
</code></pre>
<p>This configuration tells Vitest to use the Node.js environment for our tests, which is appropriate for our Express.js application.</p>
<p>We also need to update our <code>package.json</code> file to use Vitest instead of Jest for running tests. Replace the "test" script in your <code>package.json</code> with the following</p>
<pre><code class="hljsgo"><span class="hljs-string">"scripts"</span>: {
  <span class="hljs-string">"test"</span>: <span class="hljs-string">"vitest"</span>,
  <span class="hljs-string">"coverage"</span>: <span class="hljs-string">"vitest run --coverage"</span>
}
</code></pre>
<p>This change allows us to run tests with <code>npm test</code> and generate a coverage report with <code>npm run coverage</code>.</p>
<h3 id="updating-test-files">Updating Test Files</h3>
<p>Now that we have Vitest installed and set up, we need to update our existing test files. Luckily, Vitest is mostly compatible with Jest, so we only need to make a few changes. Let's update our test files to work with Vitest.</p>
<p>The first step in updating our test files is to change the import statements. Unlike Jest, which automatically makes test functions like <code>describe</code>, <code>it</code>, and <code>expect</code> available globally, Vitest requires us to import these functions explicitly (unless you've enabled globals in your Vitest config, which we did earlier).</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">import</span> { describe, it, expect } <span class="hljs-keyword">from</span> <span class="hljs-string">'vitest'</span>;
</code></pre>
<p>While Vitest aims to be compatible with Jest, there are a few syntax differences to be aware of:</p>
<ul>
<li>
<p>Vitest uses <code>test</code> and <code>it</code> interchangeably, just like Jest.</p>
</li>
<li>
<p>Vitest handles async tests in the same way as Jest, so your existing async tests should work without modification.</p>
</li>
<li>
<p>Vitest supports the same <code>beforeEach</code>, <code>afterEach</code>, <code>beforeAll</code>, and <code>afterAll</code> hooks as Jest. No changes are needed for these.</p>
</li>
</ul>
<p>If you're using Jest's mocking capabilities, you might need to make some adjustments. Vitest provides its own mocking functions that are similar to Jest's. For example:</p>
<ul>
<li>
<p>Replace <code>jest.fn()</code> with <code>vi.fn()</code></p>
</li>
<li>
<p>Replace <code>jest.spyOn()</code> with <code>vi.spyOn()</code></p>
</li>
<li>
<p>Replace <code>jest.mock()</code> with <code>vi.mock()</code></p>
</li>
</ul>
<p>Here's an example of how our updated <code>__tests__/routes.test.js</code> file might look after these changes:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">import</span> { describe, it, expect } <span class="hljs-keyword">from</span> <span class="hljs-string">'vitest'</span>;
<span class="hljs-keyword">import</span> request <span class="hljs-keyword">from</span> <span class="hljs-string">'supertest'</span>;
<span class="hljs-keyword">import</span> app <span class="hljs-keyword">from</span> <span class="hljs-string">'../app'</span>;

describe(<span class="hljs-string">'API Routes'</span>, () =&gt; {
  it(<span class="hljs-string">'GET / should return welcome message'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> request(app).get(<span class="hljs-string">'/'</span>);
    expect(res.statusCode).toBe(<span class="hljs-number">200</span>);
    expect(res.body).toHaveProperty(<span class="hljs-string">'message'</span>, <span class="hljs-string">'Welcome to the Express Jest Demo API!'</span>);
  });

  it(<span class="hljs-string">'GET /users should return list of users'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> request(app).get(<span class="hljs-string">'/users'</span>);
    expect(res.statusCode).toBe(<span class="hljs-number">200</span>);
    expect(res.body).toBeInstanceOf(<span class="hljs-built_in">Array</span>);
    expect(res.body).toHaveLength(<span class="hljs-number">2</span>);
    expect(res.body[<span class="hljs-number">0</span>]).toHaveProperty(<span class="hljs-string">'id'</span>);
    expect(res.body[<span class="hljs-number">0</span>]).toHaveProperty(<span class="hljs-string">'name'</span>);
  });

  it(<span class="hljs-string">'POST /users should create a new user'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> request(app)
      .post(<span class="hljs-string">'/users'</span>)
      .send({ <span class="hljs-attr">name</span>: <span class="hljs-string">'Test User'</span> });
    expect(res.statusCode).toBe(<span class="hljs-number">201</span>);
    expect(res.body).toHaveProperty(<span class="hljs-string">'id'</span>);
    expect(res.body).toHaveProperty(<span class="hljs-string">'name'</span>, <span class="hljs-string">'Test User'</span>);
  });

  it(<span class="hljs-string">'POST /users should return 400 if name is missing'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> request(app)
      .post(<span class="hljs-string">'/users'</span>)
      .send({});
    expect(res.statusCode).toBe(<span class="hljs-number">400</span>);
    expect(res.body).toHaveProperty(<span class="hljs-string">'error'</span>, <span class="hljs-string">'Name is required'</span>);
  });
});
</code></pre>
<p>The changes needed to switch from Jest to Vitest are small for basic tests. The test structure and assertions stay mostly the same, making the migration easy and simple.</p>
<h2 id="potential-challenges-and-solutions">Potential Challenges and Solutions</h2>
<p>While migrating from Jest to Vitest is generally straightforward, you may encounter some challenges along the way.</p>
<p><strong>Vitest has better support for ESM</strong>, which might cause issues if your project uses CommonJS modules.<br>
If you're using CommonJS (<code>require</code> syntax), you may need to update your import/export statements to use ESM syntax (<code>import</code>/<code>export</code>). Alternatively, you can configure Vitest to support CommonJS:</p>
<pre><code class="hljsjavascript"><span class="hljs-comment">// vitest.config.js</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">test</span>: {
    <span class="hljs-attr">environment</span>: <span class="hljs-string">'node'</span>
  }
}
</code></pre>
<h2 id="best-practices-post-migration">Best Practices Post-Migration</h2>
<p>Congratulations 🎉 on successfully moving your test suite from Jest to Vitest! Now that you've switched, it's time to make the most of Vitest's features and improve your test suites. Let's look at some best practices to follow after the migration.</p>
<p><strong>Utilize Concurrent Testing</strong> Vitest runs tests concurrently by default, which can significantly speed up your test suite. To take full advantage of this:</p>
<ul>
<li>
<p>Ensure your tests are isolated and don't depend on shared state.</p>
</li>
<li>
<p>Use <code>beforeEach</code> and <code>afterEach</code> hooks to set up and tear down test environments.</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">import</span> { beforeEach, afterEach, describe, it } <span class="hljs-keyword">from</span> <span class="hljs-string">'vitest'</span>;

describe(<span class="hljs-string">'My Test Suite'</span>, () =&gt; {
  beforeEach(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
    <span class="hljs-comment">// Set up test environment</span>
  });

  afterEach(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
    <span class="hljs-comment">// Clean up after each test</span>
  });

  it(<span class="hljs-string">'runs concurrently with other tests'</span>, () =&gt; {
    <span class="hljs-comment">// Your test here</span>
  });
});
</code></pre>
</li>
</ul>
<p><strong>Use Thread-based Parallelism</strong> For CPU-intensive tests, Vitest allows you to run tests in separate threads:</p>
<pre><code class="hljsjavascript"><span class="hljs-comment">// vitest.config.js</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">test</span>: {
    <span class="hljs-attr">threads</span>: <span class="hljs-literal">true</span>
  }
}
</code></pre>
<p><strong>Take Advantage of Built-in Code Coverage</strong> Vitest includes built-in code coverage reporting. Enable it in your configuration:</p>
<pre><code class="hljsjavascript"><span class="hljs-comment">// vitest.config.js</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">test</span>: {
    <span class="hljs-attr">coverage</span>: {
      <span class="hljs-attr">reporter</span>: [<span class="hljs-string">'text'</span>, <span class="hljs-string">'json'</span>, <span class="hljs-string">'html'</span>]
    }
  }
}
</code></pre>
<p><strong>Utilize Vitest UI</strong> Vitest offers a web-based UI for running and debugging tests. Start it with:</p>
<pre><code class="hljs">npx vitest --ui
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725612660637/1bd4f712-4498-437f-a709-7649bfe83d5f.png" alt=""></p>
<h2 id="conclusion">Conclusion</h2>
<p>Migrating from Jest to Vitest is easy and requires only a few changes for basic tests. Vitest has many benefits, like faster test runs, built-in TypeScript support, and easy integration with the Vite ecosystem. Although Jest is older and has a bigger community, Vitest's speed and modern features make it a great choice, especially for projects using Vite or those needing a lightweight solution.</p>
<h2 id="frequently-asked-questions"><strong>Frequently Asked Questions</strong></h2>
<h3 id="what-is-the-difference-between-jest-and-vitest-for-testing-javascript-applications">What is the difference between Jest and Vitest for testing JavaScript applications?</h3>
<p>Jest is a popular testing framework for JavaScript, especially in React apps. It has strong features like snapshot testing, mocking, and built-in code coverage. Vitest is a newer framework made for the Vite ecosystem, offering faster test runs and native TypeScript support. While Jest has a bigger community, Vitest is faster and works well with modern ES modules.</p>
<h3 id="why-should-i-switch-to-vitest-from-jest-for-vite-based-projects">Why should I switch to Vitest from Jest for Vite-based projects?</h3>
<p>Vitest is designed for Vite-based projects and provides much faster performance by using Vite’s dev server. It supports most of Jest's APIs, making migration easy while keeping compatibility. If you're using Vite, Vitest offers better integration, faster tests, and improved ES module support compared to Jest.</p>
<h3 id="how-do-i-migrate-my-jest-tests-to-vitest"><strong>How do I migrate my Jest tests to Vitest?</strong></h3>
<p>Migrating from Jest to Vitest is quite easy. First, uninstall Jest and install Vitest with its dependencies. Next, update the test configuration (e.g., <code>vitest.config.js</code>), change any <code>jest.fn()</code> to <code>vi.fn()</code>, and ensure your import statements are compatible. Most projects will need only a few code changes because Vitest supports many Jest-like APIs.</p>
<h3 id="is-vitest-faster-than-jest-for-testing">Is Vitest faster than Jest for testing?</h3>
<p>Yes, Vitest is usually faster than Jest, especially for Vite-based projects. Vitest uses Vite's hot module replacement (HMR) and dev server, which makes tests run quicker. Also, Vitest runs tests at the same time by default, which boosts performance for large test suites.</p>
<h3 id="can-vitest-be-used-for-testing-non-vite-projects-like-express-js">Can Vitest be used for testing non-Vite projects like Express.js?</h3>
<p>Yes, Vitest can be used for testing non-Vite projects, including back-end frameworks like Express.js. While Vitest is optimized for Vite, it supports Node.js environments, making it flexible for both front-end and back-end testing. You just need to configure <code>vitest.config.js</code> to use the Node environment and make a few small changes to your tests.</p>
<h3 id="how-do-i-set-up-code-coverage-in-vitest-like-i-do-in-jest">How do I set up code coverage in Vitest like I do in Jest?</h3>
<p>Vitest has built-in code coverage reporting, just like Jest. To enable it, configure the <code>vitest.config.js</code> file with coverage options such as reporters (<code>text</code>, <code>json</code>, <code>html</code>). Then, run <code>npm run coverage</code> to generate a coverage report, which helps you track test coverage in your Vitest project.</p>
]]></description>
                                                            <category>javascript</category>
                                            <category>nodejs</category>
                                            <category>react</category>
                                            <category>webdev</category>
                                                    <author><![CDATA[Keploy io]]></author>
                <guid>https://devdojo.com/12356</guid>
                <pubDate>Sun, 08 Sep 2024 23:58:24 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[What Is Acceptance Testing]]></title>
                <link>https://devdojo.com/keploy/what-is-acceptance-testing</link>
                <description><![CDATA[<p>Software testing that confirms if a system or application satisfies the necessary specifications and business needs is called acceptance testing. It is usually performed at the end of the software development life cycle, after unit testing and integration testing have been completed.</p>
<p>The main goal of acceptance testing are to :</p>
<ul>
<li>
<p>Ensure the system or software meets the specified functional and non-functional requirements.</p>
</li>
<li>
<p>Verify that the system or application satisfies the end-user's expectations and needs.</p>
</li>
<li>
<p>Confirm the system or application is ready for deployment and can be used in a production environment.</p>
</li>
</ul>
<p>Instead of the development team, end users, business stakeholders, or customers frequently conduct acceptance testing. This guarantees a dispassionate assessment of the program from the viewpoint of its users.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725265447903/c73e112b-7f05-4875-b963-6edc104e521d.jpeg" alt=""></p>
<h2 id="what-are-key-characteristics-of-acceptance-testing">What are Key Characteristics of Acceptance Testing?</h2>
<p>Acceptance tests are useful for verifying whether a software product satisfies user or customer requirements and expectations because they have a few essential features.</p>
<ul>
<li>
<p>The end-user or customer is the one who writes acceptance tests. They put a lot of effort into making sure the delivered product meets the needs of its intended users by confirming that the software behaves as expected in practical situations.</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>Clear pass/fail criteria are used in acceptance tests. The test's result ought to be unambiguous and binary, meaning it should either pass or fail. Having this clarity aids in deciding whether to release the software or not.</p>
</li>
<li>
<p>Acceptance tests should have a manageable scope even though they strive to cover every important component of the program. The objective is to test the most crucial features and functionalities rather than a large number of tests that might be challenging to manage.</p>
</li>
<li>
<p>Although acceptance tests aim to cover every significant aspect of the program, their scope should be manageable. Instead of testing numerous features and functionalities that could be difficult to manage, the goal is to test the most important ones.</p>
</li>
</ul>
<h2 id="different-types-of-acceptance-testing">Different Types of Acceptance Testing</h2>
<p>Acceptance testing can be categorised into multiple types, the below mentioned are few</p>
<ul>
<li>
<p><strong>Business Acceptance Tests (BATs)</strong>: Evaluates whether the system meets the organization's business objectives and requirements.</p>
</li>
<li>
<p><strong>User Acceptance Tests (UATs)</strong>: Validates the system from the end-user's perspective to ensure it meets their requirements.</p>
</li>
<li>
<p><strong>Regulations Acceptance Testing (RAT</strong>) : Ensures the system complies with relevant laws, regulations, and industry standards.</p>
</li>
</ul>
<p>Acceptance testing is a critical step in the software development lifecycle, as it helps to identify and address any remaining issues or defects before the system is deployed to the production environment.</p>
<h2 id="what-are-benefits-of-acceptance-testing">What are Benefits of Acceptance Testing?</h2>
<ul>
<li>
<p>Ensures the software meets user requirements by validating that the application aligns with the specific functional and non-functional requirements.</p>
</li>
<li>
<p>Reduces the risk of delivering a product that doesn't meet expectations</p>
</li>
<li>
<p>Improves communication and collaboration between stakeholders by involving the customer and business user in the testing process.</p>
</li>
</ul>
<h2 id="some-best-practices-for-acceptance-testing">Some Best Practices for Acceptance Testing</h2>
<ul>
<li>
<p><strong>Involve users/customers throughout the testing process</strong></p>
<p>Engage users or customers early and often during acceptance testing to ensure that the product aligns with their needs and expectations. Their feedback is invaluable for validating whether the software meets the defined acceptance criteria. Involving them also fosters a sense of ownership and helps in catching issues that may not be apparent to developers or testers.</p>
</li>
<li>
<p><strong>Automate acceptance tests for efficiency and consistency</strong></p>
<p>Automating acceptance tests helps to streamline the testing process, allowing for faster and more reliable execution of test cases. Automated tests can be run frequently, ensuring that new changes do not introduce regressions. This not only saves time and resources but also improves the overall consistency and repeatability of the testing process, making it easier to detect defects early in the development cycle.</p>
</li>
<li>
<p><strong>Maintain clear traceability between requirements and test cases</strong></p>
<p>Establishing a clear linkage between requirements and their corresponding test cases ensures that every user story or requirement is adequately covered by the acceptance tests. This traceability helps in verifying that all aspects of the product have been tested and nothing is overlooked.</p>
</li>
<li>
<p><strong>Continuously review and update acceptance criteria</strong></p>
<p>As the project evolves, so should the acceptance criteria. Regularly reviewing and updating these criteria ensures they remain relevant and aligned with the project’s goals.</p>
</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>Acceptance testing plays a pivotal role in ensuring that software meets both functional and non-functional requirements while aligning with the expectations of end-users and stakeholders. By validating the system through real-world scenarios and involving the customer throughout the process, acceptance testing helps to deliver a product that is ready for deployment and capable of providing value to its users.</p>
<h2 id="frequently-asked-questions"><strong>Frequently Asked Questions</strong></h2>
<h3 id="who-conducts-acceptance-testing">Who conducts Acceptance Testing?</h3>
<p>Acceptance testing is usually conducted by end-users, business stakeholders, or customers rather than the development team. This approach ensures an unbiased evaluation of the software from the users' perspective.</p>
<h3 id="can-acceptance-testing-be-automated">Can Acceptance Testing be automated?</h3>
<p>Yes, acceptance tests can be automated to increase efficiency and consistency. Automation allows for frequent execution of test cases, ensuring that new changes do not introduce regressions and helps in detecting defects early in the development cycle.</p>
<h3 id="what-are-best-practices-for-acceptance-testing">What are best practices for Acceptance Testing?</h3>
<ul>
<li>
<p>Involving users/customers throughout the testing process</p>
</li>
<li>
<p>Automating acceptance tests for efficiency and consistency</p>
</li>
<li>
<p>Maintaining clear traceability between requirements and test cases</p>
</li>
<li>
<p>Continuously reviewing and updating acceptance criteria</p>
</li>
</ul>
<h3 id="difference-between-acceptance-and-sandwich-testing">Difference between Acceptance and Sandwich Testing</h3>
<p>Acceptance Testing validates the entire system against user requirements and is performed at the end of the development cycle by end-users or stakeholders. Sandwich Testing is an integration testing method that combines top-down and bottom-up approaches to test different layers of the system simultaneously. It is usually conducted during the integration phase by developers or testers.</p>
<h3 id="how-do-you-measure-the-success-of-acceptance-testing">How do you measure the success of Acceptance Testing?</h3>
<p>The success of acceptance testing is measured by how well the software meets the predefined acceptance criteria. A successful acceptance test means the software is approved for deployment, with any identified defects either resolved or deferred by mutual agreement.</p>
<h3 id="can-acceptance-testing-be-performed-in-agile-development">Can Acceptance Testing be performed in Agile development?</h3>
<p>Yes, acceptance testing can be integrated into Agile development. In Agile, acceptance tests are often written as part of the user stories, and testing is conducted iteratively throughout the development cycle, allowing for continuous feedback and adjustments.</p>
]]></description>
                                                                    <author><![CDATA[Keploy io]]></author>
                <guid>https://devdojo.com/12350</guid>
                <pubDate>Fri, 06 Sep 2024 00:35:06 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to group array by date in PHP – Fast Tips]]></title>
                <link>https://devdojo.com/inspector/how-to-group-array-by-date-in-php-fast-tips</link>
                <description><![CDATA[<p>I use this technique to group the bug fixes array by date in the Inspector dashboard, and I thought it could be a good code snippet idea for others. I also wrote an implementation for Laravel blade templates and a more detailed implementation that supports filtering.</p>
<p>I decided to implement this code because it makes a list of items so easy to scroll based on their history.</p>
<h2 id="group-array-by-date-in-pure-php">Group array by date in pure PHP</h2>
<p>This implementation uses the <a href="https://www.php.net/manual/en/function.array-reduce.php">array_reduce</a> function. It allows to progressively create a new array where each date becomes a key, with the corresponding element as its value.</p>
<pre><code class="hljsphp">$data = [
    [<span class="hljs-string">'date'</span> =&gt; <span class="hljs-string">'2023-06-01'</span>, <span class="hljs-string">'value'</span> =&gt; <span class="hljs-number">10</span>],
    [<span class="hljs-string">'date'</span> =&gt; <span class="hljs-string">'2023-06-02'</span>, <span class="hljs-string">'value'</span> =&gt; <span class="hljs-number">20</span>],
    [<span class="hljs-string">'date'</span> =&gt; <span class="hljs-string">'2023-06-01'</span>, <span class="hljs-string">'value'</span> =&gt; <span class="hljs-number">30</span>],
    [<span class="hljs-string">'date'</span> =&gt; <span class="hljs-string">'2023-06-03'</span>, <span class="hljs-string">'value'</span> =&gt; <span class="hljs-number">40</span>],
    [<span class="hljs-string">'date'</span> =&gt; <span class="hljs-string">'2023-06-02'</span>, <span class="hljs-string">'value'</span> =&gt; <span class="hljs-number">50</span>],
];

$groupedData = array_reduce($data, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($result, $item)</span> </span>{
    $date = <span class="hljs-keyword">new</span> DateTime($item[<span class="hljs-string">'date'</span>]);
    $formattedDate = $date-&gt;format(<span class="hljs-string">'Y-m-d'</span>);
    
    <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">isset</span>($result[$formattedDate])) {
        $result[$formattedDate] = [];
    }
    
    $result[$formattedDate][] = $item;
    
    <span class="hljs-keyword">return</span> $result;
}, []); <span class="hljs-comment">// &lt;-- Start with an empty array</span>
</code></pre>
<p>Thanks to the DateTime object and the <a href="https://www.php.net/manual/en/datetime.formats.php">format</a> method you can customize the grouping logic by month, or year, by simply changing the format string: 'Y-m' for month, or 'Y' for year.</p>
<h2 id="filtering-and-grouping">Filtering and grouping</h2>
<p>You can also introduce a filter function to filter the elements before grouping them by the date field.</p>
<pre><code class="hljsphp">$groupedData = array_reduce(array_filter($data, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($item)</span> <span class="hljs-title">use</span> <span class="hljs-params">($filter)</span> </span>{
		<span class="hljs-comment">// Filter condition: keep elements with value greater than 20</span>
		<span class="hljs-keyword">return</span> $item[<span class="hljs-string">'value'</span>] &gt; $filter;
	}), 
	<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($result, $item)</span> </span>{
		$date = <span class="hljs-keyword">new</span> DateTime($item[<span class="hljs-string">'date'</span>]);
		$formattedDate = $date-&gt;format(<span class="hljs-string">'Y-m-d'</span>);
		
		<span class="hljs-keyword">if</span> (!<span class="hljs-keyword">isset</span>($result[$formattedDate])) {
			$result[$formattedDate] = [];
		}
		
		$result[$formattedDate][] = $item;
		
		<span class="hljs-keyword">return</span> $result;
	}, []);
</code></pre>
<p>Inside the callback function of array_filter(), we specify the filter condition. In this example, we keep only the elements where the ‘value’ field is greater than <code>$filter</code>. You can modify this condition based on your specific use case.</p>
<h2 id="showing-results-in-the-ui-with-laravel-blade">Showing results in the UI with Laravel blade</h2>
<p>Obviously you can take inspiration and use the same strategy in your specific technology (like Symfony + Twig, or similar).</p>
<p>To keep the data manipulation statements separated from the view I keep the filtering and grouping process at the controller level, and I implement only the data structure iteration on the template side.</p>
<p>Here is the Controller:</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"><span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>;


<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Request</span>;


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DashboardController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Controller</span>
</span>{
    <span class="hljs-comment">/**
     * The dashboard.
     *
     * <span class="hljs-doctag">@param</span> ImpersonatesUsers $impersonator
     * <span class="hljs-doctag">@return</span> \Illuminate\Contracts\View\Factory|\Illuminate\View\View
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">index</span><span class="hljs-params">(Request $request)</span>
    </span>{
		$data = <span class="hljs-keyword">$this</span>-&gt;getData();
		
		$data = array_reduce(array_filter($data, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($item)</span> <span class="hljs-title">use</span> <span class="hljs-params">($filter)</span> </span>{
			<span class="hljs-comment">// Filter condition: keep elements with value greater than 20</span>
			<span class="hljs-keyword">return</span> $item[<span class="hljs-string">'value'</span>] &gt; $filter;
		}), 
		<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($result, $item)</span> </span>{
			$date = <span class="hljs-keyword">new</span> DateTime($item[<span class="hljs-string">'date'</span>]);
			$formattedDate = $date-&gt;format(<span class="hljs-string">'Y-m-d'</span>);
			
			<span class="hljs-keyword">if</span> (!<span class="hljs-keyword">isset</span>($result[$formattedDate])) {
				$result[$formattedDate] = [];
			}
			
			$result[$formattedDate][] = $item;
			
			<span class="hljs-keyword">return</span> $result;
		}, []);
		
        <span class="hljs-keyword">return</span> view(<span class="hljs-string">'dashboard'</span>, compact(<span class="hljs-string">'data'</span>));
    }
}
</code></pre>
<p>And here is the blade view:</p>
<pre><code class="hljsphp">&lt;ul&gt;
    @<span class="hljs-keyword">foreach</span> ($groupedData <span class="hljs-keyword">as</span> $date =&gt; $items)
        &lt;li&gt;
            &lt;strong&gt;{{ $date }}&lt;/strong&gt;
            &lt;ul&gt;
                @<span class="hljs-keyword">foreach</span> ($items <span class="hljs-keyword">as</span> $item)
                    &lt;li&gt;Value: {{ $item[<span class="hljs-string">'value'</span>] }}&lt;/li&gt;
                @<span class="hljs-keyword">endforeach</span>
            &lt;/ul&gt;
        &lt;/li&gt;
    @<span class="hljs-keyword">endforeach</span>
&lt;/ul&gt;
</code></pre>
<h2 id="group-a-laravel-collection-by-date">Group a Laravel Collection by date</h2>
<p>Thanks to the builtin utilities provided by the Laravel Collection class it's really straightforward:</p>
<pre><code class="hljsphp">$groupedData = collect($data)-&gt;groupBy(<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($item)</span> </span>{
    <span class="hljs-keyword">return</span> Carbon::parse($item-&gt;date)-&gt;format(<span class="hljs-string">'Y-m-d'</span>);
});
</code></pre>
<p>You can follow me on <a href="https://www.linkedin.com/in/valeriobarbera/">Linkedin</a> or <a href="https://x.com/Barbalerio">X</a>. I post about building my SaaS business.</p>
<h2 id="monitor-your-php-application-for-free">Monitor your PHP application for free</h2>
<p>Inspector is a Code Execution Monitoring tool specifically designed for software developers. You don't need to install anything at the server level, just install the <strong><a href="https://github.com/inspector-apm">composer package</a></strong> and you are ready to go.</p>
<p>Inspector is super easy and PHP friendly. You can try our <a href="https://github.com/inspector-apm/inspector-laravel">Laravel</a> or <a href="https://github.com/inspector-apm/inspector-symfony">Symfony</a> package.</p>
<p>If you are looking for HTTP monitoring, database query insights, and the ability to forward alerts and notifications into your preferred messaging environment, try Inspector for free. <a href="https://app.inspector.dev/register">Register your account</a>.</p>
<p>Or learn more on the website: https://inspector.dev</p>
<p><img src="https://cdn.devdojo.com/images/september2024/segments-statistics.png" alt="segments-statistics.png"></p>
]]></description>
                                                            <category>php</category>
                                            <category>laravel</category>
                                            <category>programming</category>
                                            <category>webdev</category>
                                            <category>symfony</category>
                                                    <author><![CDATA[ ]]></author>
                <guid>https://devdojo.com/12345</guid>
                <pubDate>Wed, 04 Sep 2024 01:53:05 -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[17 Open Source Alternatives to Your Favorite Software and Apps 🔥👨‍💻]]></title>
                <link>https://devdojo.com/madzadev/17-open-source-alternatives-to-your-favorite-software-and-apps</link>
                <description><![CDATA[<p>In the dynamic world of software and applications, many users are discovering the power and flexibility of open-source alternatives.</p>
<p>Whether you're looking to cut costs, avoid vendor lock-in, or explore new tools, open-source options offer customizable solutions that rival their proprietary counterparts.</p>
<p>In this article, we'll explore 17 powerful open-source tools that can effectively replace some of the most commonly used software and apps.</p>
<p>From diagramming and design platforms to API development and workflow automation, these alternatives are designed to enhance your productivity and workflow.</p>
<p>Each tool will include a direct link, category, description, top features, preview images, and links to the code and website for the initial impression.</p>
<ol>
<li><a href="https://github.com/jgraph/drawio">Drawio</a></li>
</ol>
<hr>
<blockquote>
<p>Category: Diagrams</p>
<p>Open Source Alternative to: LucidChart</p>
</blockquote>
<p>Drawio is a free, open-source diagramming tool used for creating flowcharts, network diagrams, and other visuals.</p>
<p>It enhances productivity by offering an intuitive interface and seamless integration with commonly used platforms like Google Drive and GitHub.</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>Reasons to choose:</p>
<ul>
<li>Free to use with no subscription costs.</li>
<li>Can be used offline, unlike many cloud-based diagramming tools.</li>
<li>Integrates seamlessly with popular platforms.</li>
<li>Open-source, allowing access to code and improve it.</li>
<li>User-friendly interface suitable for both beginners and professionals.</li>
</ul>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fbKTvhNr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.drawio.com/assets/img/blog/custom-menu-custom-default-theme.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fbKTvhNr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.drawio.com/assets/img/blog/custom-menu-custom-default-theme.png" alt="Drawio"></a></p>
<p>👩‍💻 Github link: <a href="https://github.com/jgraph/drawio">https://github.com/jgraph/drawio</a></p>
<p>⭐ Github stars: 40.3K</p>
<p>💻 Website link: <a href="https://app.diagrams.net/">https://app.diagrams.net/</a></p>
<ol start="2">
<li><a href="https://github.com/penpot/penpot">Penpot</a></li>
</ol>
<hr>
<blockquote>
<p>Category: Design</p>
<p>Open Source Alternative to: Figma</p>
</blockquote>
<p>Penpot is an open-source design and prototyping platform for cross-domain teams.</p>
<p>It streamlines design workflows by enabling real-time collaboration and flexible design systems.</p>
<p>Reasons to choose:</p>
<ul>
<li>Open-source with no vendor lock-in.</li>
<li>Supports real-time collaboration across teams.</li>
<li>Cross-platform compatibility, working in any web browser.</li>
<li>Flexible design systems for better team collaboration.</li>
<li>Great community support and continuous updates.</li>
</ul>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qi8mTqp9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1724331005420/52c8d371-717a-49ba-a549-d6de3b3450f9.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qi8mTqp9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1724331005420/52c8d371-717a-49ba-a549-d6de3b3450f9.png" alt="Penpot"></a></p>
<p>👩‍💻 Github link: <a href="https://github.com/penpot/penpot">https://github.com/penpot/penpot</a></p>
<p>⭐ Github stars: 31.8K</p>
<p>💻 Website link: <a href="https://penpot.app/">https://penpot.app/</a></p>
<ol start="3">
<li><a href="https://github.com/hoppscotch/hoppscotch">Hoppscotch</a></li>
</ol>
<hr>
<blockquote>
<p>Category: API Development</p>
<p>Open Source Alternative to: Postman</p>
</blockquote>
<p>Hoppscotch is a lightweight and fast open-source API development tool.</p>
<p>It boosts productivity with its user-friendly interface for testing APIs without the need for complex setups.</p>
<p>Reasons to choose:</p>
<ul>
<li>Lightweight and faster than most API testing tools.</li>
<li>Open-source with active community contributions.</li>
<li>Supports a wide range of API types, including REST, GraphQL, and WebSocket.</li>
<li>No installation required; runs in the browser.</li>
<li>Simple and intuitive user interface.</li>
</ul>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U-WgwbLQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/hoppscotch/hoppscotch/raw/main/packages/hoppscotch-common/public/images/banner-dark.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U-WgwbLQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/hoppscotch/hoppscotch/raw/main/packages/hoppscotch-common/public/images/banner-dark.png" alt="Hoppscotch"></a></p>
<p>👩‍💻 Github link: <a href="https://github.com/hoppscotch/hoppscotch">https://github.com/hoppscotch/hoppscotch</a></p>
<p>⭐ Github stars: 63.4K</p>
<p>💻 Website link: <a href="https://hoppscotch.io/">https://hoppscotch.io/</a></p>
<ol start="4">
<li><a href="https://github.com/supertokens/supertokens-core">Supertokens</a></li>
</ol>
<hr>
<blockquote>
<p>Category: Authentication</p>
<p>Open Source Alternative to: Okta, Auth0</p>
</blockquote>
<p>Supertokens is an open-source authentication solution designed to secure web and mobile applications.</p>
<p>It simplifies user authentication and session management, enhancing both security and productivity.</p>
<p>Reasons to choose:</p>
<ul>
<li>Easy integration with popular front-end frameworks.</li>
<li>Granular session management with strong security features.</li>
<li>Support for multi-factor authentication.</li>
<li>Customizable authentication workflows.</li>
<li>Scalable architecture suitable for large applications.</li>
</ul>
<p><a href="https://camo.githubusercontent.com/3a5fb3d27d6e5e717d4d2426e46560eab6806ac7953f16694a506449b105e20d/68747470733a2f2f7375706572746f6b656e732e636f6d2f696d672f6172636869746563747572652f73656c665f686f737465645f67656e657269632e706e67"><img src="https://camo.githubusercontent.com/3a5fb3d27d6e5e717d4d2426e46560eab6806ac7953f16694a506449b105e20d/68747470733a2f2f7375706572746f6b656e732e636f6d2f696d672f6172636869746563747572652f73656c665f686f737465645f67656e657269632e706e67" alt="Supertokens"></a></p>
<p>👩‍💻 Github link: <a href="https://github.com/supertokens/supertokens-core">https://github.com/supertokens/supertokens-core</a></p>
<p>⭐ Github stars: 12.5K</p>
<p>💻 Website link: <a href="https://supertokens.com/">https://supertokens.com/</a></p>
<ol start="5">
<li><a href="https://github.com/n8n-io/n8n">n8n</a></li>
</ol>
<hr>
<blockquote>
<p>Category: Workflow Automation</p>
<p>Open Source Alternative to: Zapier, Make</p>
</blockquote>
<p>n8n is an open-source workflow automation tool that connects different apps and services.</p>
<p>It enhances productivity by automating repetitive tasks and integrating with a wide range of platforms.</p>
<p>Reasons to choose:</p>
<ul>
<li>Visual workflow editor with drag-and-drop functionality.</li>
<li>Pre-built integrations with various services.</li>
<li>Powerful conditional logic and branching in workflows.</li>
<li>Ability to self-host, ensuring full data privacy and control.</li>
<li>Highly customizable with custom nodes and scripts.</li>
</ul>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0EQ3QK0R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/n8n-io/n8n/master/assets/n8n-screenshot.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0EQ3QK0R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/n8n-io/n8n/master/assets/n8n-screenshot.png" alt="n8n"></a></p>
<p>👩‍💻 Github link: <a href="https://github.com/n8n-io/n8n">https://github.com/n8n-io/n8n</a></p>
<p>⭐ Github stars: 44K</p>
<p>💻 Website link: <a href="https://n8n.io/">https://n8n.io/</a></p>
<ol start="6">
<li><a href="https://github.com/umami-software/umami">umami</a></li>
</ol>
<hr>
<blockquote>
<p>Category: Web Analytics</p>
<p>Open Source Alternative to: Google Analytics</p>
</blockquote>
<p>umami is an open-source web analytics solution that respects user privacy.</p>
<p>It provides detailed insights into website performance without compromising on data privacy.</p>
<p>Reasons to choose:</p>
<ul>
<li>Lightweight and fast, reducing website load times.</li>
<li>Simple, clean interface for easy navigation and data interpretation.</li>
<li>Real-time data tracking and reporting.</li>
<li>All data is anonymized, and no personal information is ever collected.</li>
<li>Customizable dashboard and tracking capabilities.</li>
</ul>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rd1mEst4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1724331224086/862a2bca-aeea-4f5c-8193-1b3e1808281f.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rd1mEst4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1724331224086/862a2bca-aeea-4f5c-8193-1b3e1808281f.png" alt="umami"></a></p>
<p>👩‍💻 Github link: <a href="https://github.com/umami-software/umami">https://github.com/umami-software/umami</a></p>
<p>⭐ Github stars: 21.1K</p>
<p>💻 Website link: <a href="https://umami.is/">https://umami.is/</a></p>
<ol start="7">
<li><a href="https://github.com/haiwen/seafile">Seafile</a></li>
</ol>
<hr>
<blockquote>
<p>Category: File Hosting</p>
<p>Open Source Alternative to: Google Drive</p>
</blockquote>
<p>Seafile is an open-source file hosting and synchronization platform.</p>
<p>It improves productivity by enabling secure file sharing and collaboration within teams.</p>
<p>Reasons to choose:</p>
<ul>
<li>High-speed file synchronization with delta file transfer.</li>
<li>Built-in file encryption for secure storage.</li>
<li>Supports advanced permissions and role management.</li>
<li>Flexible deployment options, including cross-platform support.</li>
<li>Integrated wiki and document collaboration features.</li>
</ul>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GjLqZkX7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://startpack.ru/repository/application/894/image/3643.jpg"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GjLqZkX7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://startpack.ru/repository/application/894/image/3643.jpg" alt="Seafile"></a></p>
<p>👩‍💻 Github link: <a href="https://github.com/haiwen/seafile">https://github.com/haiwen/seafile</a></p>
<p>⭐ Github stars: 12K</p>
<p>💻 Website link: <a href="https://www.seafile.com/">https://www.seafile.com/</a></p>
<ol start="8">
<li><a href="https://github.com/ceph/ceph">ceph</a></li>
</ol>
<hr>
<blockquote>
<p>Category: Storage</p>
<p>Open Source Alternative to: Amazon S3</p>
</blockquote>
<p>ceph is an open-source distributed storage system that provides object, block, and file storage.</p>
<p>It supports massive scalability and high availability, making it ideal for cloud environments.</p>
<p>Reasons to choose:</p>
<ul>
<li>Highly scalable, supporting petabytes of data across thousands of nodes.</li>
<li>Provides unified storage for object, block, and file systems.</li>
<li>Self-healing capabilities to ensure data integrity.</li>
<li>Efficient use of resources with erasure coding and data compression.</li>
<li>Supports multi-site replication and disaster recovery.</li>
</ul>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c2xLsSoF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1724333686663/ec4c6286-9579-4d51-8b1a-61e2395c8025.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c2xLsSoF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1724333686663/ec4c6286-9579-4d51-8b1a-61e2395c8025.png" alt="ceph"></a></p>
<p>👩‍💻 Github link: <a href="https://github.com/ceph/ceph">https://github.com/ceph/ceph</a></p>
<p>⭐ Github stars: 13.7K</p>
<p>💻 Website link: <a href="https://ceph.io/">https://ceph.io/</a></p>
<ol start="9">
<li><a href="https://github.com/ShareX/ShareX">ShareX</a></li>
</ol>
<hr>
<blockquote>
<p>Category: Screen Capture</p>
<p>Open Source Alternative to: Lightshot</p>
</blockquote>
<p>ShareX is a free and open-source tool for screen capture and file sharing.</p>
<p>It enhances productivity with powerful screenshot, GIF creation, and file upload capabilities.</p>
<p>Reasons to choose:</p>
<ul>
<li>Extensive capture modes, including region captures.</li>
<li>Built-in image editor with advanced annotation tools.</li>
<li>Supports automated workflows for post-capture processing.</li>
<li>Quick upload to a wide range of file hosting services.</li>
<li>Customizable keyboard shortcuts for efficient usage.</li>
</ul>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FtOfPt4Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://getsharex.com/img/ShareX_Screenshot.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FtOfPt4Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://getsharex.com/img/ShareX_Screenshot.png" alt="ShareX"></a></p>
<p>👩‍💻 Github link: <a href="https://github.com/ShareX/ShareX">https://github.com/ShareX/ShareX</a></p>
<p>⭐ Github stars: 28.9K</p>
<p>💻 Website link: <a href="https://getsharex.com/">https://getsharex.com/</a></p>
<ol start="10">
<li><a href="https://github.com/strapi/strapi">Strapi</a></li>
</ol>
<hr>
<blockquote>
<p>Category: Headless CMS</p>
<p>Open Source Alternative to: Contentful</p>
</blockquote>
<p>Strapi is an open-source headless CMS for managing content in any digital experience.</p>
<p>It streamlines content creation and management with a highly customizable admin panel.</p>
<p>Reasons to choose:</p>
<ul>
<li>Fully customizable API with flexible content structures.</li>
<li>User-friendly admin panel for content management.</li>
<li>Supports RESTful and GraphQL APIs out-of-the-box.</li>
<li>Role-based access control with advanced permissions.</li>
<li>Extensible with plugins to add new features and functionalities.</li>
</ul>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qZG6EPA7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://docs.strapi.io/img/assets/content-manager/content-manager_single-type.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qZG6EPA7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://docs.strapi.io/img/assets/content-manager/content-manager_single-type.png" alt="Strapi"></a></p>
<p>👩‍💻 Github link: <a href="https://github.com/strapi/strapi">https://github.com/strapi/strapi</a></p>
<p>⭐ Github stars: 62.4K</p>
<p>💻 Website link: <a href="https://strapi.io/">https://strapi.io/</a></p>
<ol start="11">
<li><a href="https://github.com/RocketChat/Rocket.Chat">Rocket.Chat</a></li>
</ol>
<hr>
<blockquote>
<p>Category: Communication</p>
<p>Open Source Alternative to: Slack, Microsoft Teams, Discord</p>
</blockquote>
<p><a href="http://Rocket.Chat">Rocket.Chat</a> is an open-source communication platform for team collaboration.</p>
<p>It boosts productivity by enabling real-time messaging, video calls, and file sharing.</p>
<p>Reasons to choose:</p>
<ul>
<li>Highly customizable user interface and features.</li>
<li>Supports a wide range of communication methods, including messaging, voice, and video.</li>
<li>Strong security features, including end-to-end encryption.</li>
<li>Integrates well with third-party apps and services.</li>
<li>Scalable for organizations of all sizes.</li>
</ul>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XUw3cDiA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.prod.website-files.com/611a19b9853b7414a0f6b3f6/617064b93b5df6410cfc6b55_Home.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XUw3cDiA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.prod.website-files.com/611a19b9853b7414a0f6b3f6/617064b93b5df6410cfc6b55_Home.png" alt="Rocket.Chat"></a></p>
<p>👩‍💻 Github link: <a href="https://github.com/RocketChat/Rocket.Chat">https://github.com/RocketChat/Rocket.Chat</a></p>
<p>⭐ Github stars: 39.8K</p>
<p>💻 Website link: <a href="https://rocket.chat/">https://rocket.chat/</a></p>
<ol start="12">
<li><a href="https://github.com/discourse/discourse">Discourse</a></li>
</ol>
<hr>
<blockquote>
<p>Category: Community</p>
<p>Open Source Alternative to: Tribe, Circle</p>
</blockquote>
<p>Discourse is an open-source discussion platform that powers online communities.</p>
<p>It enhances community engagement with features like threaded discussions, notifications, and moderation tools.</p>
<p>Reasons to choose:</p>
<ul>
<li>Threaded conversations for clear and organized discussions.</li>
<li>Strong moderation tools with automated spam filtering.</li>
<li>Real-time notifications and engagement features.</li>
<li>Easy-to-use interface that adapts well to mobile devices.</li>
<li>Plugin architecture for expanding functionalities.</li>
</ul>
<p><a href="https://camo.githubusercontent.com/60a80b06686be82f766a2bb0555ecbcb186b54081d2593bad3ce5341d296cd53/68747470733a2f2f6769746875622d70726f64756374696f6e2d757365722d61737365742d3632313064662e73332e616d617a6f6e6177732e636f6d2f353836323230362f3236313231353839382d61653935663936332d356162342d343530392d623837612d6639663665396131303962662e706e67"><img src="https://camo.githubusercontent.com/60a80b06686be82f766a2bb0555ecbcb186b54081d2593bad3ce5341d296cd53/68747470733a2f2f6769746875622d70726f64756374696f6e2d757365722d61737365742d3632313064662e73332e616d617a6f6e6177732e636f6d2f353836323230362f3236313231353839382d61653935663936332d356162342d343530392d623837612d6639663665396131303962662e706e67" alt="Discourse"></a></p>
<p>👩‍💻 Github link: <a href="https://github.com/discourse/discourse">https://github.com/discourse/discourse</a></p>
<p>⭐ Github stars: 41.5K</p>
<p>💻 Website link: <a href="https://www.discourse.org/">https://www.discourse.org/</a></p>
<ol start="13">
<li><a href="https://github.com/Peppermint-Lab/peppermint">Peppermint</a></li>
</ol>
<hr>
<blockquote>
<p>Category: Support</p>
<p>Open Source Alternative to: Zendesk</p>
</blockquote>
<p>Peppermint is an open-source support ticketing system designed for your businesses.</p>
<p>It streamlines customer support by organizing and prioritizing customer inquiries efficiently.</p>
<p>Reasons to choose:</p>
<ul>
<li>Simple and intuitive ticket management system.</li>
<li>Fast setup and easy to use with a minimal learning curve.</li>
<li>Ability to customize fields and workflows to fit business needs.</li>
<li>Provides insights with basic analytics and reporting tools.</li>
<li>Integrates with existing communication tools like email.</li>
</ul>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kO6ia7h5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1724333793958/9ecf95ea-7beb-474e-b074-af32e0c1fd8b.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kO6ia7h5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1724333793958/9ecf95ea-7beb-474e-b074-af32e0c1fd8b.png" alt="Peppermint"></a></p>
<p>👩‍💻 Github link: <a href="https://github.com/Peppermint-Lab/peppermint">https://github.com/Peppermint-Lab/peppermint</a></p>
<p>⭐ Github stars: 1.8K</p>
<p>💻 Website link: <a href="https://peppermint.sh/">https://peppermint.sh/</a></p>
<ol start="14">
<li><a href="https://github.com/prometheus/prometheus">Prometheus</a></li>
</ol>
<hr>
<blockquote>
<p>Category: Monitoring</p>
<p>Open Source Alternative to: Datadog</p>
</blockquote>
<p>Prometheus is an open-source monitoring and alerting toolkit for complex systems.</p>
<p>It improves productivity by offering powerful data collection and visualization capabilities for system performance.</p>
<p>Reasons to choose:</p>
<ul>
<li>Powerful multi-dimensional data model with flexible queries.</li>
<li>High performance, designed for large-scale monitoring.</li>
<li>Real-time alerting with configurable rules.</li>
<li>Integration with Grafana for advanced visualizations.</li>
<li>Built-in service discovery for dynamic environments.</li>
</ul>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4uRWDPdy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1724331973021/b49dff2b-fd72-4af6-8f44-176653bd444a.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4uRWDPdy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1724331973021/b49dff2b-fd72-4af6-8f44-176653bd444a.png" alt="Prometheus"></a></p>
<p>👩‍💻 Github link: <a href="https://github.com/prometheus/prometheus">https://github.com/prometheus/prometheus</a></p>
<p>⭐ Github stars: 54.4K</p>
<p>💻 Website link: <a href="https://prometheus.io/">https://prometheus.io/</a></p>
<ol start="15">
<li><a href="https://github.com/nocodb/nocodb">NocoDB</a></li>
</ol>
<hr>
<blockquote>
<p>Category: Database</p>
<p>Open Source Alternative to: Airtable</p>
</blockquote>
<p>NocoDB is an open-source no-code platform that turns databases into smart spreadsheets.</p>
<p>It boosts productivity by enabling users to create applications without coding knowledge.</p>
<p>Reasons to choose:</p>
<ul>
<li>User-friendly interface that mimics traditional spreadsheets.</li>
<li>Extensive integration with a variety of databases and apps.</li>
<li>Rich field types, including formulas, file attachments, and relations.</li>
<li>Collaboration features like commenting and real-time updates.</li>
<li>Advanced filtering, sorting, and grouping options.</li>
</ul>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GynBNDjP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1724332018612/2947b052-f74a-4c98-8a03-d8f58005302f.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GynBNDjP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1724332018612/2947b052-f74a-4c98-8a03-d8f58005302f.png" alt="NocoDB"></a></p>
<p>👩‍💻 Github link: <a href="https://github.com/nocodb/nocodb">https://github.com/nocodb/nocodb</a></p>
<p>⭐ Github stars: 44.4K</p>
<p>💻 Website link: <a href="https://www.nocodb.com/">https://www.nocodb.com/</a></p>
<ol start="16">
<li><a href="https://github.com/dbeaver/dbeaver">Dbeaver</a></li>
</ol>
<hr>
<blockquote>
<p>Category: Database Management</p>
<p>Open Source Alternative to: Toad</p>
</blockquote>
<p>Dbeaver is an open-source database management tool that supports a wide range of databases.</p>
<p>It enhances productivity with its universal database interface and advanced SQL editor.</p>
<p>Reasons to choose:</p>
<ul>
<li>Universal database tool supporting a wide variety of databases.</li>
<li>Advanced SQL editor with features like autocompletion and syntax highlighting.</li>
<li>Visual query builder for non-technical users.</li>
<li>Ability to manage and visualize database schemas.</li>
<li>Supports data import/export in multiple formats.</li>
</ul>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wUhub4Rl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dbeaver.io/wp-content/uploads/2018/03/erd.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wUhub4Rl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dbeaver.io/wp-content/uploads/2018/03/erd.png" alt="Dbeaver"></a></p>
<p>👩‍💻 Github link: <a href="https://github.com/dbeaver/dbeaver">https://github.com/dbeaver/dbeaver</a></p>
<p>⭐ Github stars: 38.9K</p>
<p>💻 Website link: <a href="https://dbeaver.io/">https://dbeaver.io/</a></p>
<ol start="17">
<li><a href="https://github.com/dokku/dokku">Dokku</a></li>
</ol>
<hr>
<blockquote>
<p>Category: Deployment</p>
<p>Open Source Alternative to: Heroku, Render</p>
</blockquote>
<p>Dokku is an open-source PaaS solution that simplifies application deployment.</p>
<p>It streamlines deployment processes with its Heroku-like interface for managing and scaling apps.</p>
<p>Reasons to choose:</p>
<ul>
<li>Simple deployment process similar to Heroku's git push model.</li>
<li>Supports Docker containers for application management.</li>
<li>Scalable, with support for plugins to add more features.</li>
<li>Easy to set up and manage via a command-line interface.</li>
<li>Comprehensive documentation and strong community support.</li>
</ul>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KwvlZ_X6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1724333224819/d07a5536-5f56-4d0b-b5b7-a113d59d7207.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KwvlZ_X6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1724333224819/d07a5536-5f56-4d0b-b5b7-a113d59d7207.png" alt="Dokku"></a></p>
<p>👩‍💻 Github link: <a href="https://github.com/dokku/dokku">https://github.com/dokku/dokku</a></p>
<p>⭐ Github stars: 26.5K</p>
<p>💻 Website link: <a href="https://dokku.com/">https://dokku.com/</a></p>
<p>Writing has always been my passion and it gives me pleasure to help and inspire people. If you have any questions, feel free to reach out!</p>
<p>Make sure to receive the best resources, tools, productivity tips, and career growth tips I discover by subscribing to <a href="https://madzadev.substack.com/">my newsletter</a>!</p>
<p>Also, connect with me on <a href="https://twitter.com/madzadev">Twitter</a>, <a href="https://www.linkedin.com/in/madzadev/">LinkedIn</a>, and <a href="https://github.com/madzadev">GitHub</a>!</p>
]]></description>
                                                                    <author><![CDATA[ ]]></author>
                <guid>https://devdojo.com/12305</guid>
                <pubDate>Fri, 23 Aug 2024 01:01:06 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Understand MicroService-Based Architecture]]></title>
                <link>https://devdojo.com/harendra21/understand-microservice-based-architecture</link>
                <description><![CDATA[<blockquote>
<p>Breaking Down Complex Systems: A Simple Guide to Mastering MicroService-Based Architecture</p>
</blockquote>
<p>There are many types of software design patterns available, MicroService-based architecture is one of the most common architectures that is widely used by big giants such as  <strong>Google</strong>,  <strong>Netflix</strong>,  <strong>Amazon</strong>, and  <strong>Facebook</strong>. So basically you can say that we prefer to use the micro-service architecture to support large-scale systems due to its benefits, which we will see in a few minutes.</p>
<p>Understand MicroService-Based Architecture</p>
<p>Before beginning with this article I want to let you know about how many types of main software architecture are available, below is the list of them —</p>
<ol>
<li>Monolithic</li>
<li><strong>Microservices</strong></li>
<li>Serverless</li>
<li>Event-Driven</li>
<li>Layered (N-Tier)</li>
<li>Service-Oriented Architecture (SOA)</li>
<li>Component-Based</li>
<li>Peer-to-Peer (P2P)</li>
<li>Domain-Driven Design (DDD)</li>
</ol>
<h2 id="what-are-microservices">What are  <strong>Microservices?</strong></h2>
<p>The microservice-based architecture is a design pattern in which we divide the larger services into smaller independent services and those services can communicate with each other with the help of well-defined APIs. Every service has its roles and responsibilities and it focuses only on that business function. Some characteristics of microservice include:</p>
<ul>
<li><strong>Modularity</strong>:  We are developing the services in independent modules that are easy to develop, maintain, reuse, and scale.</li>
<li><strong>Decentralization</strong>: This is another key characteristic of microservice Architecture, different services can use different technologies such as databases, programming languages, etc.</li>
<li><strong>Scalability</strong>: We can scale independent services based on the demand which is more useful to optimize the resources.</li>
<li><strong>Autonomy</strong>: We can develop, deploy, and run each service independently, which helps us to improve flexibility and resilience.</li>
<li><strong>Continuous Deployment</strong>: The independent running service is more flexible to update and maintain, which helps to achieve continuous integration and deployment.</li>
</ul>
<p>Below is the diagram for MicroService-Based Architecture, let’s understand the each and every individual components of it.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/0*ZP9UFk22x3EAIReg" alt="MicroService-Based Architecture"></p>
<p>MicroService Architecture</p>
<h2 id="static-files-and-cdn"><strong>Static Files and CDN</strong></h2>
<p>Static files in websites are nothing but files like javascript (.js), CSS (.css), images (.jpg, .png, .webp, or .gif), and videos (.mp4). To store those files we are using the storage buckets and to serve them we are using  <a href="https://www.cloudflare.com/learning/cdn/what-is-a-cdn/">CDN</a>  (content delivery network) which is another type of networking system that delivers the files from the nearest 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>
<h2 id="load-balancer">Load Balancer</h2>
<p>A load balancer is a device or software that is used to distribute the incoming traffic across the services to prevent the primary server from getting overloaded. Some main features of load balancer are as follows-</p>
<ul>
<li><strong>Traffic Distribution:</strong> It helps to distribute the incoming requests across multiple servers to prevent the single server from becoming a bottleneck</li>
<li><strong>High Availability:</strong> It also helps the application to keep working in server failure by redirecting the traffic to a healthy one.</li>
<li><strong>Scalability</strong>: Load balancer allows to addition of more servers and distribution of the load across them at the time surge (high requests)</li>
<li><strong>Performance Optimization</strong>: It improves the performance by reducing the latency of the requests and making them more available.</li>
</ul>
<h2 id="api-gateway">API Gateway</h2>
<p>The API Gateway is a server that acts as an entry point for any API request and it helps to manage and route the request to the appropriate backend service in an application. It captures incoming requests and routes them to the appropriate backend service. The key features of API gateway include -</p>
<ul>
<li><strong>Request Routing</strong>: It redirects the API request to the appropriate microservice based on its URL or params (query params or route params).</li>
<li><strong>Load Balancing</strong>: It also distributes the requests to multiple instances of micro-service to keep the load evenly distributed and ensure high availability.</li>
<li><strong>Authentication and Authorization</strong>: The load balancer also acts as the route guard for the API requests by validating the credentials and permissions before providing them access.</li>
<li><strong>Rate Limiting</strong>: Api gateway can prevent the number of requests based on the IP or other configuration to prevent overuse or attack.</li>
<li><strong>Aggregation</strong>: The response from different micro-services can also be combined into one single response.</li>
</ul>
<h2 id="identity-provider">Identity Provider</h2>
<p>An Identity Provider is a service used to manage and authenticate user identities. It provides a centralized system to authenticate the user’s access and various micro-services. The key features of Identity Provider include -</p>
<ul>
<li><strong>Authentication:</strong> Authentication is a process that verifies the  <strong>authenticity</strong>  of a  <strong>user’s identity</strong>  to access the feature.</li>
<li><strong>Single Sign-On (SSO):</strong> SSO is the process of  authentication that allows users to authenticate using a single identity at many places.</li>
<li><strong>User Management:</strong> This refers to the management of users based on their roles, restricting or allowing them to use a particular resource.</li>
</ul>
<h2 id="message-broker">Message Broker</h2>
<p>It is a middleware between different services that is used to communicate internally to exchange data. It acts as an intermediary that receives, stores, and sends them from consumer to producer.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/1*zSiNZjobli89hkHUdn2gEQ.gif" alt="Kafka (Message Broker)"></p>
<p>Kafka (Message Broker)</p>
<p>Key functions include:</p>
<ul>
<li><strong>Message Routing:</strong> It is a key requirement for system and service integration. One pattern that can be used to separate a message source from its destination is message routing.</li>
<li><strong>Message Queuing:</strong> A message queue is a form of asynchronous service-to-service communication used in serverless and microservices architectures.</li>
<li><strong>Message Transformation:</strong> In microservices, message transformation refers to transforming messages that are sent back and forth between services into a format that is understandable to each service.</li>
</ul>
<h2 id="service-registry-and-discovery">Service Registry and Discovery</h2>
<p>Services can locate and communicate with each other dynamically thanks to service registry and discovery. Service Discovery allows services to find and connect based on the information supplied by the registry, guaranteeing scalability and resilience in the system. The Service Registry maintains track of all available service instances and their locations.</p>
<p><strong>There are two types of Service Discovery available</strong></p>
<ul>
<li>Client-Side Service Discovery</li>
<li>Server-Side Service Discovery</li>
</ul>
<h2 id="service-coordination-zookeeper">Service Coordination (Zookeeper)</h2>
<p>ZooKeeper is a distributed, open-source coordination service for distributed applications. It exposes a simple set of primitives that distributed applications can build upon to implement higher level services for synchronization, configuration maintenance, and groups and naming. It is designed to be easy to program to, and uses a data model styled after the familiar directory tree structure of file systems. It runs in Java and has bindings for both Java and C.[<a href="https://zookeeper.apache.org/doc/r3.5.4-beta/zookeeperOver.html">source</a>]</p>
<p><img src="https://miro.medium.com/v2/resize:fit:600/0*nCKQi6fYH8AWIIRA.jpg" alt=""></p>
<h2 id="database">Database</h2>
<p>An organized set of electronically recorded data is called a database. Any kind of data, including text, numbers, pictures, videos, and files, can be included. Data may be stored, retrieved, and edited using software known as a database management system (DBMS).</p>
<h2 id="pro-cons">Pro/Cons</h2>
<p><strong>Pros of Microservices:</strong></p>
<ol>
<li><strong>Scalability:</strong>  We can scale up and down the services independently based on their usages or demand</li>
<li><strong>Flexibility:</strong>  Microservice allows to use the of multiple technologies together such as multiple programming languages as per their advantages such as Python, golang, or Java.</li>
<li><strong>Resilience:</strong>  As the services are running individually they are affecting less during the outage, and your entire system will not crash.</li>
</ol>
<p><strong>Cons of Microservices:</strong></p>
<ol>
<li><strong>Complexity:</strong>  As we need to develop and run the services individually, it increases the  <strong>codebase</strong>  and  <strong>complexity</strong> of the overall system which is a little bit hard to manage.</li>
<li><strong>Communication Overhead:</strong>  In microservice, we have to establish the communication between the service to exchange information which is a little bit overhead here.</li>
<li><strong>Data Consistency:</strong>  Maintaining consistency across distributed services can be challenging, as each service may have its own database and data model.</li>
</ol>
<p>Thank you for reading this blog post, if you are interested in learning more about golang or full-stack development,  <strong><em>consider following me</em></strong>. I keep writing posts about it regularly.</p>
<ul>
<li><strong><em>Medium —</em></strong> <a href="https://medium.com/@harendra21"><strong><em>https://medium.com/@harendra21</em></strong></a></li>
<li><strong><em>Twitter (X) —</em></strong> <a href="https://x.com/harendraverma2"><strong><em>https://x.com/harendraverma2</em></strong></a></li>
</ul>
</p>]]></description>
                                                                    <author><![CDATA[Harendra Kumar]]></author>
                <guid>https://devdojo.com/12300</guid>
                <pubDate>Thu, 22 Aug 2024 02:04:30 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Optimizing Node.js Code Coverage with NYC in Docker Containers]]></title>
                <link>https://devdojo.com/keploy/optimizing-nodejs-code-coverage-with-nyc-in-docker-containers</link>
                <description><![CDATA[<p>There are particular difficulties in getting NYC coverage from Node.js operating in Docker containers. This blog discusses the procedures needed to operate Node.js clusters in Docker and produce reliable code coverage reports.</p>
<h2 id="how-to-get-coverage-nativally"><strong>How to Get Coverage nativally?</strong></h2>
<p>Running NYC locally is straightforward with a simple command:</p>
<pre><code class="hljsgo"><span class="hljs-string">"start"</span>: <span class="hljs-string">"nyc node ./dist/main.js"</span>,
</code></pre>
<p>However, there are more stages involved in getting support within Docker. Let’s explore the nuances of this.</p>
<h2 id="what-are-the-challenges-with-signal-handling-in-docker-containers"><strong>What are the Challenges with Signal Handling in Docker Containers ?</strong></h2>
<p>Sending a docker an interrupt signal is quite a deal in our case. Reason? Instead of running a binary I’m running a script(to talk about later)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724074475003/289e9ffa-2b63-4bbb-b2c9-1b43fbe4e936.png" alt=""></p>
<p>You see my dear readers, what I’ve to face? my docker container doesn’t die, but why? Its because of the signal Handling in Docker and shell behaviour.</p>
<ol>
<li>
<p><strong>PID 1 in Containers</strong>:In a Docker container, the CMD or ENTRYPOINT process runs as PID 1. This process is responsible for handling system signals.When using <strong>sh</strong> or <strong>bash</strong> to run a script, <strong>sh</strong> becomes the PID 1 process inside the container.</p>
</li>
<li>
<p><strong>Signal Forwarding</strong>: The shell (sh) does not automatically forward signals to child processes. Instead, it handles the signals itself, often not propagating them to the processes it spawns.<br>
When you press Ctrl+C, Docker sends a SIGINT signal to the PID 1 process. If sh is the PID 1 process, it might not forward the signal to the actual application process (e.g., node, yarn start).</p>
</li>
<li>
<p><strong>Zombie Processes</strong>: If the shell does not correctly forward the signal to the child processes, those processes continue running. This can lead to zombie processes and an unresponsive 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>
</li>
</ol>
<p>How to solve this? A simple trap signal and passing it to child processes should work fine.</p>
<pre><code class="hljsperl"><span class="hljs-comment">#!/bin/sh</span>

set -a
. <span class="hljs-string">".env.$ENVIRONMENT_NAME"</span>
set +a

<span class="hljs-comment"># Function to handle SIGINT signal</span>
handle_int() {
    echo <span class="hljs-string">"SIGINT received, forwarding to child process..."</span>
    <span class="hljs-keyword">kill</span> -INT <span class="hljs-string">"$child"</span> <span class="hljs-number">2</span>&gt;<span class="hljs-regexp">/dev/null</span>
    <span class="hljs-keyword">exit</span> <span class="hljs-number">0</span>
}

<span class="hljs-comment"># Trap SIGINT signal</span>
trap <span class="hljs-string">'handle_int'</span> INT TERM

<span class="hljs-keyword">sleep</span> <span class="hljs-number">10</span>  <span class="hljs-comment"># Ensure services like DB are up</span>

<span class="hljs-comment"># Start yarn and get its PID</span>
yarn start &amp;

child=$!
echo <span class="hljs-string">"Started yarn with PID $child"</span>

<span class="hljs-comment"># Wait for child process to finish</span>
<span class="hljs-keyword">wait</span> <span class="hljs-string">"$child"</span>

<span class="hljs-comment"># Copy nyc output to host directory (optional, if you need it)</span>
</code></pre>
<p>From within the container this is what all processes look like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720809961795/548872fc-b1a7-455d-a867-48bc30743b04.png" alt=""></p>
<p>But is this <strong>trap and send</strong> enough?<strong>No it is not</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720809983527/5baffbf8-bd13-4b35-b45b-c3a7e9cb8287.png" alt=""></p>
<p>It surely does kill the container but doesn’t do the graceful shutdown. It kills the PID 1 and moves ahead <strong>without</strong> child processes to wrap up, something we’re really looking here for.</p>
<p>Reason for this issue:-</p>
<ol>
<li>
<p><strong>Shell Limitations</strong>: The default shell (<code>sh</code>) might not forward signals to child processes correctly. Instead, it might terminate itself, leaving child processes orphaned.</p>
</li>
<li>
<p><strong>Signal Propagation</strong>: The shell script (<a href="http://migrate-and-run.sh"><code>run.sh</code></a>) directly receives the signal and may not propagate it to the <code>yarn start</code> process effectively.</p>
</li>
</ol>
<p>Then how to send the signal to the yarn start which gets our coverage?</p>
<h2 id="what-are-effective-solutions-for-handling-signals">What are effective solutions for Handling Signals ?</h2>
<p>We need to ensure that the default kill signal is converted to a stop signal, and signal is passed to child processes. Finally, we also intend to copy data from docker image to host machine. We can follow the below steps to handle these signals and ensure that the interrupt signal reaches the process groups.</p>
<ol>
<li>
<p>Using dumb-init for Better Signal Handling</p>
</li>
<li>
<p>Handling Signal Traps in Shell Scripts</p>
</li>
<li>
<p>Copying NYC Coverage Data from Docker Container</p>
</li>
</ol>
<h3 id="using-dumb-init-for-better-signal-handling">Using dumb-init for Better Signal Handling</h3>
<p>Dumb-init is a simple process supervisor designed to run as PID 1 inside Docker containers. It handles reaping zombie processes and forwards signals to all processes in its process group.</p>
<p>Why <code>dumb-init</code> helps:</p>
<ul>
<li>
<p><strong>Signal Forwarding:</strong> When the Docker container receives a SIGINT signal, <code>dumb-init</code> forwards it to the shell script and all child processes.</p>
</li>
<li>
<p><strong>Process Management:</strong> <code>dumb-init</code> ensures proper reaping of zombie processes, which the default shell might not handle well.</p>
</li>
</ul>
<pre><code class="hljspowershell">&lt;STAGE<span class="hljs-literal">-1</span><span class="hljs-literal">-BUILD</span>&gt;
...
&lt;STAGE<span class="hljs-literal">-2</span><span class="hljs-literal">-BUILD</span>&gt;
...

<span class="hljs-comment"># Set working directory</span>
ENTRYPOINT [<span class="hljs-string">"dumb-init"</span>, <span class="hljs-string">"--"</span>]

<span class="hljs-comment"># RUN echo "hi"</span>
STOPSIGNAL SIGINT

<span class="hljs-comment"># Set entrypoint and command</span>
CMD [ <span class="hljs-string">"sh"</span>,<span class="hljs-string">"./scripts/migrate-and-run.sh"</span>]

<span class="hljs-comment"># Expose port 9000</span>
EXPOSE <span class="hljs-number">9000</span>
</code></pre>
<p>Thus the usage of <em>dumb-init</em> helps us gracefully kill the yarn start and its subsequent workers.</p>
<p><strong>Ensuring NYC Coverage Data is Written Correctly</strong></p>
<p>One key mistake I made was handling NYC coverage across different locations: building the coverage in one place, copying it to another for execution, and reading the report in yet another. This inconsistency was problematic</p>
<h3 id="how-to-handle-signal-traps-in-shell-scripts"><strong>How to handle Signal Traps in Shell Scripts ?</strong></h3>
<p>As you saw earlier, I run the build file, which includes build functions and maps them to my code. When running a multi-stage build, the directory may differ, yet the build still works.</p>
<p>In the example below, the application is built in the <code>app-build</code> directory. In the second stage of the Docker build, it is executed via the script. For building purposes, the exact location doesn't matter much, but for NYC, it does.</p>
<p>NYC relies on consistent paths to correctly map coverage data to the source files. Therefore, ensuring that the context of the build, execution, and reading paths remain the same is crucial for accurate NYC coverage reporting.</p>
<p>So when I try to get the coverage I see something like, reason? NYC doesn’t has the context of the code in the second stage of build, therefore I get no line coverage.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720810021728/69ba2ea4-8ebb-4c0c-a23f-ca58fcbcac21.png" alt=""></p>
<p>So our dockerfile needs the same path that’s in pre build vs post build something looks like</p>
<pre><code class="hljspowershell">&lt;STAGE<span class="hljs-literal">-1</span><span class="hljs-literal">-BUILD</span>&gt;

...
&lt;STAGE<span class="hljs-literal">-2</span><span class="hljs-literal">-BUILD</span>&gt;
...

WORKDIR  /app<span class="hljs-literal">-build</span>
<span class="hljs-comment"># Set working directory</span>
ENTRYPOINT [<span class="hljs-string">"dumb-init"</span>, <span class="hljs-string">"--"</span>]

<span class="hljs-comment"># RUN echo "hi"</span>
STOPSIGNAL SIGINT

<span class="hljs-comment"># Set entrypoint and command</span>
CMD [ <span class="hljs-string">"sh"</span>,<span class="hljs-string">"./scripts/migrate-and-run.sh"</span>]
</code></pre>
<p>And our coverage in docker container looks like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720810047058/7359122f-8f6d-4c6d-b39a-1e62eb3925b2.png" alt=""></p>
<p>Lets copy this data locally and see what happens:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720810066396/04d090b2-1352-40ef-9d6b-2b05a558dcc4.png" alt=""></p>
<p>But this isn’t what I saw in screenshot above. why is the coverage coming to be zero?</p>
<p>You see the build and working context of directory isn’t same as my local machine thus NYC isn’t able to fetch me the correct coverage. How to solve it?<br>
Keep the current path of repository, build and the working directory same. As you see I take the path as argument and build over it to keep the context always same for my NYC to output correct data.</p>
<pre><code class="hljsruby">&lt;STAGE-<span class="hljs-number">1</span>-BUILD&gt;
ENV APP_PATH=${<span class="hljs-symbol">APP_PATH:</span>-<span class="hljs-regexp">/default/path</span>} 
...
&lt;STAGE-<span class="hljs-number">2</span>-BUILD&gt;
...
<span class="hljs-comment"># Set working directory</span>
WORKDIR ${APP_PATH}
ENTRYPOINT [<span class="hljs-string">"dumb-init"</span>, <span class="hljs-string">"--"</span>]

<span class="hljs-comment"># RUN echo "hi"</span>
STOPSIGNAL SIGINT

<span class="hljs-comment"># Set entrypoint and command</span>
CMD [ <span class="hljs-string">"sh"</span>,<span class="hljs-string">"./scripts/migrate-and-run.sh"</span>]
</code></pre>
<p>The command to build the docker image:</p>
<pre><code class="hljspowershell">sudo <span class="hljs-literal">-E</span> docker build -<span class="hljs-literal">-build</span><span class="hljs-literal">-arg</span> ENVIRONMENT_NAME=docker -<span class="hljs-literal">-build</span><span class="hljs-literal">-arg</span> BUILD_NAME=docker -<span class="hljs-literal">-build</span><span class="hljs-literal">-arg</span> APP_PATH=<span class="hljs-variable">$</span>(pwd) <span class="hljs-literal">-t</span> custom_app .
</code></pre>
<p>The result of this, when I copy the data on my local machine it gives me the desired output.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720810091472/cb6f6c08-6fcf-4809-bca8-a5cb6aa99727.png" alt=""></p>
<p><strong>The importance of script</strong></p>
<p>In script we catch the signal and handle it. If it wasn’t done we would see something like this ~</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720810105769/56629f51-8935-4af7-ab53-080f5f9956b9.png" alt=""></p>
<p>What just happened here now? You see when program is stopped NYC needs some time to write the final json file from memory to local folder. This extra small delay is something we won’t wait for. This causes this <a href="https://stackoverflow.com/questions/78496025/how-do-i-hold-stopping-docker-container-to-get-nyc-report-sleep-doesnt-work/78499162#78499162">error</a>.</p>
<pre><code class="hljsperl"><span class="hljs-comment">#!/bin/sh</span>
<span class="hljs-comment"># Function to handle SIGINT signal</span>
handle_int() {
    echo <span class="hljs-string">"SIGINT received, forwarding to child process..."</span>
    <span class="hljs-keyword">kill</span> -INT <span class="hljs-string">"$child"</span> <span class="hljs-number">2</span>&gt;<span class="hljs-regexp">/dev/null</span>
    echo <span class="hljs-string">"Exiting after delay..."</span>
    <span class="hljs-keyword">exit</span> <span class="hljs-number">0</span>
}

<span class="hljs-comment"># Trap SIGINT signal</span>
trap <span class="hljs-string">'handle_int'</span> INT TERM

<span class="hljs-keyword">sleep</span> <span class="hljs-number">10</span>  <span class="hljs-comment"># Ensure services like DB are up</span>

<span class="hljs-comment"># Start yarn and get its PID</span>
yarn start &amp;

child=$!
echo <span class="hljs-string">"Started yarn with PID $child"</span>

<span class="hljs-comment"># Wait for child process to finish</span>
<span class="hljs-keyword">wait</span> <span class="hljs-string">"$child"</span>

<span class="hljs-comment"># Copy nyc output to host directory (optional, if you need it)</span>
</code></pre>
<p>To solve the above code and wait for child processes to die with an update handle function.</p>
<pre><code class="hljsperl"><span class="hljs-comment">#!/bin/sh</span>
<span class="hljs-comment"># Function to handle SIGINT signal</span>
handle_int() {
    echo <span class="hljs-string">"SIGINT received, forwarding to child process..."</span>
    <span class="hljs-keyword">kill</span> -INT <span class="hljs-string">"$child"</span> <span class="hljs-number">2</span>&gt;<span class="hljs-regexp">/dev/null</span>
    echo <span class="hljs-string">"Waiting for child process to exit..."</span>
    <span class="hljs-keyword">wait</span> <span class="hljs-string">"$child"</span>
    echo <span class="hljs-string">"Child process exited. Waiting for NYC coverage data to be fully written..."</span>
    <span class="hljs-keyword">sleep</span> <span class="hljs-number">10</span>  <span class="hljs-comment"># Wait for 50 seconds</span>
    echo <span class="hljs-string">"Exiting after delay..."</span>
    <span class="hljs-keyword">exit</span> <span class="hljs-number">0</span>
}

<span class="hljs-comment"># Trap SIGINT signal</span>
trap <span class="hljs-string">'handle_int'</span> INT TERM

<span class="hljs-keyword">sleep</span> <span class="hljs-number">10</span>  <span class="hljs-comment"># Ensure services like DB are up</span>

<span class="hljs-comment"># Start yarn and get its PID</span>
yarn start &amp;

child=$!
echo <span class="hljs-string">"Started yarn with PID $child"</span>

<span class="hljs-comment"># Wait for child process to finish</span>
<span class="hljs-keyword">wait</span> <span class="hljs-string">"$child"</span>

<span class="hljs-comment"># Copy nyc output to host directory (optional, if you need it)</span>
</code></pre>
<h3 id="how-to-copy-nyc-coverage-data-from-docker-container"><strong>How to Copy NYC Coverage Data from Docker Container ?</strong></h3>
<p>Would a normal volume mounting work? Nope, it won’t because when you mount the file NYC clears the old files and then you will find some log like this</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720810123346/a82aa4d7-0aad-4117-93f2-1710ef4af54c.png" alt=""></p>
<p>So either you can set the cleaning to be false something like this.</p>
<pre><code class="hljsgo">    <span class="hljs-string">"start"</span>: <span class="hljs-string">"nyc --clean=false --max-old-space-size=4096 --reporter=lcov --reporter=text  node ./dist/main.js"</span>,
</code></pre>
<p>Or instead of this you can manually copy the file using docker command.</p>
<pre><code class="hljspowershell">docker cp custom_apps:<span class="hljs-variable">$</span>(pwd)/.nyc_output <span class="hljs-variable">$</span>(pwd)/.nyc_output
</code></pre>
<h2 id="conclusion"><strong>Conclusion</strong></h2>
<ol>
<li>
<p>Passing SIGINT to Container and Its Sub-Processes<br>
When running Node.js applications in Docker containers, it’s crucial to handle SIGINT signals correctly. Using a process manager like dumb-init ensures that SIGINT signals are forwarded to all child processes. This allows for a clean shutdown of the application, ensuring that all necessary cleanup tasks are performed.</p>
</li>
<li>
<p>Setting the Correct Context of Location for NYC to Calculate Coverage<br>
NYC, the code coverage tool for Node.js, relies on having a consistent file path for both the build and execution phases. When using multi-stage Docker builds, make sure that the paths used in each stage are identical. This prevents issues with NYC being unable to locate source files, which can result in inaccurate coverage reports.</p>
</li>
<li>
<p>Adding Delay in Docker Stop to Ensure Data is Copied Out<br>
Adding a delay when stopping a Docker container ensures that all data, such as NYC coverage reports, is fully written and can be copied out. This can be achieved by trapping SIGINT signals in the container’s entry script, forwarding the signal to child processes, and waiting for a short period before exiting.</p>
</li>
<li>
<p>Incase of docker-compose handling the docker containers make sure to use below code in dockerfile.</p>
</li>
</ol>
<pre><code class="hljs">
STOPSIGNAL SIGINT
</code></pre>
<p>The above code snippet will help you send the interrupt signal to the docker container so that the containers is interrupt instead of terminated.</p>
<p>Our final dockerfile looks something like this:</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>
# <span class="hljs-selector-tag">Set</span> <span class="hljs-selector-tag">working</span> <span class="hljs-selector-tag">directory</span>
<span class="hljs-selector-tag">WORKDIR</span> <span class="hljs-selector-tag">APP_PATH</span>
<span class="hljs-selector-tag">COPY</span> . .

<span class="hljs-selector-tag">ENTRYPOINT</span> <span class="hljs-selector-attr">[<span class="hljs-string">"dumb-init"</span>, <span class="hljs-string">"--"</span>]</span>

<span class="hljs-selector-tag">STOPSIGNAL</span> <span class="hljs-selector-tag">SIGINT</span>

# <span class="hljs-selector-tag">Set</span> <span class="hljs-selector-tag">entrypoint</span> <span class="hljs-selector-tag">and</span> <span class="hljs-selector-tag">command</span>
<span class="hljs-selector-tag">CMD</span> <span class="hljs-selector-attr">[ <span class="hljs-string">"sh"</span>,<span class="hljs-string">"./scripts/migrate-and-run.sh"</span>]</span>

# <span class="hljs-selector-tag">Expose</span> <span class="hljs-selector-tag">port</span> 9000
<span class="hljs-selector-tag">EXPOSE</span> 9000
</code></pre>
<h2 id="faqs">FAQs</h2>
<h3 id="why-doesn-t-my-docker-container-stop-when-i-press-ctrl-c"><strong>Why doesn’t my Docker container stop when I press Ctrl+C?</strong></h3>
<p>This often happens because the shell script running as PID 1 in the container doesn’t propagate the signal to child processes. The parent process terminates but leaves the child processes running, leading to zombie processes. Using a process manager like <code>dumb-init</code> ensures proper signal forwarding and process management.</p>
<h3 id="how-do-i-ensure-that-nyc-code-coverage-reports-are-generated-correctly-in-docker"><strong>How do I ensure that NYC code coverage reports are generated correctly in Docker?</strong></h3>
<p>Ensure that the file paths in the Docker container are consistent between the build and runtime stages. NYC relies on file paths to map coverage data to source files. By keeping the paths consistent, NYC can accurately calculate coverage.</p>
<h3 id="what-is-dumb-init-and-why-should-i-use-it"><strong>What is</strong> <code>dumb-init</code>, and why should I use it?</h3>
<p><code>dumb-init</code> is a simple process manager designed to run as PID 1 inside containers. It forwards signals to all child processes and reaps zombie processes. This ensures clean shutdowns for Node.js applications in Docker and that all cleanup tasks, including NYC coverage report generation, are completed before the container stops.</p>
<h3 id="how-do-i-prevent-nyc-from-clearing-coverage-data-when-i-stop-a-docker-container"><strong>How do I prevent NYC from clearing coverage data when I stop a Docker container?</strong></h3>
<p>You can set the <code>--clean=false</code> flag in the NYC command to prevent it from clearing coverage data. Alternatively, you can manually copy the coverage files using Docker commands, ensuring that the coverage data is preserved.</p>
<h3 id="what-is-the-correct-way-to-handle-signals-in-docker-when-using-a-shell-script"><strong>What is the correct way to handle signals in Docker when using a shell script?</strong></h3>
<p>You should trap signals like <code>SIGINT</code> in your shell script and forward them to child processes. This can be done by using a signal handler that sends the signal to child processes and waits for them to finish before exiting. This ensures proper shutdown and coverage report generation.</p>
<h3 id="how-do-i-ensure-that-nyc-coverage-data-is-saved-before-a-docker-container-exits"><strong>How do I ensure that NYC coverage data is saved before a Docker container exits?</strong></h3>
<p>Add a small delay in your signal handler before the container shuts down to ensure all NYC coverage data is fully written. This delay gives NYC the time it needs to save data from memory to disk, preventing incomplete coverage reports.</p>
]]></description>
                                                            <category>javascript</category>
                                            <category>nodejs</category>
                                            <category>webdev</category>
                                                    <author><![CDATA[Keploy io]]></author>
                <guid>https://devdojo.com/12297</guid>
                <pubDate>Thu, 22 Aug 2024 00:18:38 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[How To Write Maintainable Go Code?]]></title>
                <link>https://devdojo.com/harendra21/how-to-write-maintainable-go-code</link>
                <description><![CDATA[<p>Hi everyone, Gophers You have to write code that can be maintained when dealing with golang. Simplicity, readability, and clarity are the fundamental components of maintainable code; these qualities help new team members grasp your work. When an individual departs, it facilitates their ability to swiftly and effortlessly continue where they left off in terms of code. If you want to write more maintainable code, you might want to think about applying these strategies.</p>
<h2 id="keep-the-main-as-small-as-possible">Keep the <code>main</code> as small as possible</h2>
<p>As we all know the main is the first goroutine function at the start of your go program, so we call it  <strong><em>main goroutine</em></strong>. It is unique because the main package is compiled into an executable program rather than being treated like a regular package by the compiler and has exported names. The main function (<code>main.main</code>), which serves as a  <strong><em>Go program’s entry point</em></strong>, is located inside the main package. The primary function and package are expected to perform as little as feasible.</p>
<p>It is strongly advised not to implement the business logic inside the main package and instead to drive the program with <code>main.main</code> since it is  <strong><em>difficult to write tests</em></strong>  for the code inside <code>main.main</code>. The structure and maintainability of the software are enhanced by separating the driver and business logic into different packages.</p>
<pre><code class="hljsgo"><span class="hljs-keyword">package</span> main  
  
<span class="hljs-keyword">import</span> (  
 <span class="hljs-string">"fmt"</span>  
 <span class="hljs-string">"log"</span>  
)  
  
<span class="hljs-comment">// main should only handle high-level orchestration  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {  
 <span class="hljs-comment">// Initialize the application  </span>
 app, err := initializeApp()  
 <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {  
  log.Fatalf(<span class="hljs-string">"Failed to initialize the app: %v"</span>, err)  
 }  
  
 <span class="hljs-comment">// Run the application  </span>
 <span class="hljs-keyword">if</span> err := app.run(); err != <span class="hljs-literal">nil</span> {  
  log.Fatalf(<span class="hljs-string">"Application error: %v"</span>, err)  
 }  
}  
  
<span class="hljs-comment">// Application struct encapsulates the application's state  </span>
<span class="hljs-keyword">type</span> Application <span class="hljs-keyword">struct</span> {  
 config <span class="hljs-keyword">string</span> <span class="hljs-comment">// example field  </span>
}  
  
<span class="hljs-comment">// initializeApp initializes the application and returns an instance of it  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">initializeApp</span><span class="hljs-params">()</span> <span class="hljs-params">(*Application, error)</span></span> {  
 <span class="hljs-comment">// Simulate loading configuration  </span>
 config, err := loadConfig()  
 <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {  
  <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err  
 }  
  
 <span class="hljs-comment">// Create and return an Application instance  </span>
 <span class="hljs-keyword">return</span> &amp;Application{config: config}, <span class="hljs-literal">nil</span>  
}  
  
<span class="hljs-comment">// run starts the application logic  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(app *Application)</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> <span class="hljs-title">error</span></span> {  
 <span class="hljs-comment">// Simulate running the application  </span>
 fmt.Println(<span class="hljs-string">"Running application with config:"</span>, app.config)  
 <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>  
}  
  
<span class="hljs-comment">// loadConfig simulates loading the configuration for the application  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">loadConfig</span><span class="hljs-params">()</span> <span class="hljs-params">(<span class="hljs-keyword">string</span>, error)</span></span> {  
 <span class="hljs-comment">// Simulate configuration loading  </span>
 config := <span class="hljs-string">"App Configuration"</span>  
 <span class="hljs-keyword">return</span> config, <span class="hljs-literal">nil</span>  
}
</code></pre>
<h2 id="always-use-meaningful-names">Always use meaningful names</h2>
<p>In Golang, it is crucial to provide variables, functions, structs and interfaces meaningful names. Thus, we have selected significant names and clarify what they stand for. Do not just pick names at random that have no connection to the topic. Though picking a name might occasionally be very difficult, you will not regret it because it makes your code easier to understand and read for both you and other people.</p>
<p><strong>Example with bad names</strong></p>
<pre><code class="hljsgo"><span class="hljs-keyword">package</span> main  
  
<span class="hljs-keyword">import</span> (  
 <span class="hljs-string">"fmt"</span>  
 <span class="hljs-string">"math"</span>  
)  
  
<span class="hljs-comment">// m function performs some calculations and prints results  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">m</span><span class="hljs-params">()</span></span> {  
 r := <span class="hljs-number">5.0</span>  
 a := cA(r)  
 fmt.Printf(<span class="hljs-string">"The result is %.2f\n"</span>, a)  
  
 s := <span class="hljs-number">4.0</span>  
 b := sA(s)  
 fmt.Printf(<span class="hljs-string">"The result is %.2f\n"</span>, b)  
}  
  
<span class="hljs-comment">// cA calculates something related to circles  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">cA</span><span class="hljs-params">(r <span class="hljs-keyword">float64</span>)</span> <span class="hljs-title">float64</span></span> {  
 <span class="hljs-keyword">return</span> math.Pi * r * r  
}  
  
<span class="hljs-comment">// sA calculates something related to squares  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">sA</span><span class="hljs-params">(s <span class="hljs-keyword">float64</span>)</span> <span class="hljs-title">float64</span></span> {  
 <span class="hljs-keyword">return</span> s * s  
}
</code></pre>
<p>You will see that in this example, the names do not indicate what we hope to accomplish in this section of code, which is highly problematic for both reliable and well-written code. Nobody will understand what we want to do with this code when they see this, and eventually, it will be too late for you to grasp either.</p>
<p><strong>Example with meaningful names</strong></p>
<pre><code class="hljsgo"><span class="hljs-keyword">package</span> main  
  
<span class="hljs-keyword">import</span> (  
 <span class="hljs-string">"fmt"</span>  
 <span class="hljs-string">"math"</span>  
)  
  
<span class="hljs-comment">// main function orchestrates the calculation and output  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {  
 radius := <span class="hljs-number">5.0</span>  
 circleArea := calculateCircleArea(radius)  
 fmt.Printf(<span class="hljs-string">"The area of the circle with radius %.2f is %.2f\n"</span>, radius, circleArea)  
  
 sideLength := <span class="hljs-number">4.0</span>  
 squareArea := calculateSquareArea(sideLength)  
 fmt.Printf(<span class="hljs-string">"The area of the square with side length %.2f is %.2f\n"</span>, sideLength, squareArea)  
}  
  
<span class="hljs-comment">// calculateCircleArea computes the area of a circle given its radius  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">calculateCircleArea</span><span class="hljs-params">(radius <span class="hljs-keyword">float64</span>)</span> <span class="hljs-title">float64</span></span> {  
 <span class="hljs-keyword">return</span> math.Pi * radius * radius  
}  
  
<span class="hljs-comment">// calculateSquareArea computes the area of a square given the length of one side  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">calculateSquareArea</span><span class="hljs-params">(sideLength <span class="hljs-keyword">float64</span>)</span> <span class="hljs-title">float64</span></span> {  
 <span class="hljs-keyword">return</span> sideLength * sideLength  
}
</code></pre>
<h2 id="try-to-write-meaningful-comments-wherever-possible">Try to write meaningful comments wherever possible</h2>
<p>The only method to give a sense of what you want to do with the code is to provide comments, which are a more accurate approach to understanding what is occurring with the code. As a result, you must include thorough comments with every significant part of the code.</p>
<p>Not only is it crucial to write comments, but we also need to update the comments anytime we update the code to avoid being misled. Block comments and inline  <a href="https://golang.withcodeexample.com/blog/golang-documentation-and-comments-guide/"><strong><em>comments</em></strong></a>  are both supported in Golang. Additionally, as it is illogical to explain self-explanatory code, I would caution against making excessive comments on the 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="hljsgo"><span class="hljs-keyword">package</span> main  
  
<span class="hljs-keyword">import</span> (  
 <span class="hljs-string">"errors"</span>  
 <span class="hljs-string">"fmt"</span>  
)  
  
<span class="hljs-comment">// main is the entry point of the application  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {  
 numbers := []<span class="hljs-keyword">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>}  
  
 <span class="hljs-comment">// Calculate the sum of the slice elements  </span>
 sum := calculateSum(numbers)  
 fmt.Printf(<span class="hljs-string">"Sum of numbers: %d\n"</span>, sum)  
  
 <span class="hljs-comment">// Find the maximum value in the slice  </span>
 max, err := findMax(numbers)  
 <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {  
  fmt.Printf(<span class="hljs-string">"Error: %v\n"</span>, err)  
 } <span class="hljs-keyword">else</span> {  
  fmt.Printf(<span class="hljs-string">"Maximum value: %d\n"</span>, max)  
 }  
}  
  
<span class="hljs-comment">// calculateSum adds up all the integers in the provided slice  </span>
<span class="hljs-comment">// It returns the total sum of the elements  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">calculateSum</span><span class="hljs-params">(nums []<span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {  
 sum := <span class="hljs-number">0</span>  
  
 <span class="hljs-comment">// Iterate over each number in the slice and add it to the sum  </span>
 <span class="hljs-keyword">for</span> _, num := <span class="hljs-keyword">range</span> nums {  
  sum += num  
 }  
  
 <span class="hljs-keyword">return</span> sum  
}  
  
<span class="hljs-comment">// findMax returns the maximum value in the provided slice of integers  </span>
<span class="hljs-comment">// If the slice is empty, it returns an error  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">findMax</span><span class="hljs-params">(nums []<span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(<span class="hljs-keyword">int</span>, error)</span></span> {  
 <span class="hljs-comment">// Check if the slice is empty  </span>
 <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(nums) == <span class="hljs-number">0</span> {  
  <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>, errors.New(<span class="hljs-string">"slice is empty, cannot determine maximum value"</span>)  
 }  
  
 <span class="hljs-comment">// Assume the first element is the maximum  </span>
 max := nums[<span class="hljs-number">0</span>]  
  
 <span class="hljs-comment">// Iterate over the slice to find the actual maximum  </span>
 <span class="hljs-keyword">for</span> _, num := <span class="hljs-keyword">range</span> nums[<span class="hljs-number">1</span>:] {  
  <span class="hljs-keyword">if</span> num &gt; max {  
   max = num  
  }  
 }  
  
 <span class="hljs-keyword">return</span> max, <span class="hljs-literal">nil</span>  
}
</code></pre>
<h2 id="don-t-repeat-the-piece-of-code">Don’t repeat the piece of code</h2>
<p>Writing the same code over and over again is a very terrible habit in all programming languages, not just Golang. The  <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself#:~:text=%22Don't%20repeat%20yourself%22,redundancy%20in%20the%20first%20place."><strong>DRY</strong></a>  (Do not Repeat Yourself) concept explicitly forbids using this technique. I am going to make a separate function if I need the same code in at least two different places.</p>
<p>Repeatedly writing the same code may cause overhead when it comes time to update it, forcing us to change it everywhere and sometimes missing it altogether.</p>
<p><strong>Example of bad practice</strong></p>
<pre><code class="hljsperl"><span class="hljs-keyword">package</span> main  
  
import (  
 <span class="hljs-string">"fmt"</span>  
)  
  
// main function demonstrates the bad practice of code repetition  
func main() {  
 <span class="hljs-regexp">//</span> Repeating the calculation <span class="hljs-keyword">for</span> different sets of numbers  
 num1, num2 := <span class="hljs-number">3</span>, <span class="hljs-number">5</span>  
 sum1 := num1 + num2  
 product1 := num1 * num2  
 fmt.Printf(<span class="hljs-string">"Sum of %d and %d is: %d\n"</span>, num1, num2, sum1)  
 fmt.Printf(<span class="hljs-string">"Product of %d and %d is: %d\n"</span>, num1, num2, product1)  
  
 num3, num4 := <span class="hljs-number">10</span>, <span class="hljs-number">20</span>  
 sum2 := num3 + num4  
 product2 := num3 * num4  
 fmt.Printf(<span class="hljs-string">"Sum of %d and %d is: %d\n"</span>, num3, num4, sum2)  
 fmt.Printf(<span class="hljs-string">"Product of %d and %d is: %d\n"</span>, num3, num4, product2)  
  
 num5, num6 := <span class="hljs-number">7</span>, <span class="hljs-number">9</span>  
 sum3 := num5 + num6  
 product3 := num5 * num6  
 fmt.Printf(<span class="hljs-string">"Sum of %d and %d is: %d\n"</span>, num5, num6, sum3)  
 fmt.Printf(<span class="hljs-string">"Product of %d and %d is: %d\n"</span>, num5, num6, product3)  
}
</code></pre>
<p><strong>Example of good practice</strong></p>
<pre><code class="hljsgo"><span class="hljs-keyword">package</span> main  
  
<span class="hljs-keyword">import</span> (  
 <span class="hljs-string">"fmt"</span>  
)  
  
<span class="hljs-comment">// main function demonstrates the good practice of avoiding code repetition  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {  
 <span class="hljs-comment">// Using a reusable function to calculate and display results  </span>
 displayResults(<span class="hljs-number">3</span>, <span class="hljs-number">5</span>)  
 displayResults(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>)  
 displayResults(<span class="hljs-number">7</span>, <span class="hljs-number">9</span>)  
}  
  
<span class="hljs-comment">// displayResults calculates the sum and product of two numbers and prints the results  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">displayResults</span><span class="hljs-params">(a, b <span class="hljs-keyword">int</span>)</span></span> {  
 sum := a + b  
 product := a * b  
 fmt.Printf(<span class="hljs-string">"Sum of %d and %d is: %d\n"</span>, a, b, sum)  
 fmt.Printf(<span class="hljs-string">"Product of %d and %d is: %d\n"</span>, a, b, product)  
}
</code></pre>
<h2 id="don-t-use-deep-nesting-in-your-code">Don’t use deep nesting in your code</h2>
<p>Everyone may find it annoying when there are too many nested codes used because it makes the code harder to read. Therefore, it is quite challenging to understand code — whether it was developed by us or by someone else — when reading hierarchical code. Always strive to keep the code from repeatedly nesting.</p>
<p><strong>Example of bad practice</strong></p>
<pre><code class="hljsgo"><span class="hljs-keyword">package</span> main  
  
<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>  
  
<span class="hljs-comment">// main function demonstrates the bad practice of deep nesting  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {  
 number := <span class="hljs-number">15</span>  
  
 <span class="hljs-keyword">if</span> number &gt; <span class="hljs-number">0</span> {  
  <span class="hljs-keyword">if</span> number%<span class="hljs-number">2</span> == <span class="hljs-number">0</span> {  
   <span class="hljs-keyword">if</span> number &gt; <span class="hljs-number">10</span> {  
    fmt.Println(<span class="hljs-string">"The number is positive, even, and greater than 10."</span>)  
   } <span class="hljs-keyword">else</span> {  
    fmt.Println(<span class="hljs-string">"The number is positive, even, and 10 or less."</span>)  
   }  
  } <span class="hljs-keyword">else</span> {  
   <span class="hljs-keyword">if</span> number &gt; <span class="hljs-number">10</span> {  
    fmt.Println(<span class="hljs-string">"The number is positive, odd, and greater than 10."</span>)  
   } <span class="hljs-keyword">else</span> {  
    fmt.Println(<span class="hljs-string">"The number is positive, odd, and 10 or less."</span>)  
   }  
  }  
 } <span class="hljs-keyword">else</span> {  
  fmt.Println(<span class="hljs-string">"The number is not positive."</span>)  
 }  
}
</code></pre>
<p><strong>Example of good practice</strong></p>
<pre><code class="hljsgo"><span class="hljs-keyword">package</span> main  
  
<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>  
  
<span class="hljs-comment">// main function demonstrates the good practice of avoiding deep nesting  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {  
 number := <span class="hljs-number">15</span>  
  
 <span class="hljs-keyword">if</span> number &lt;= <span class="hljs-number">0</span> {  
  fmt.Println(<span class="hljs-string">"The number is not positive."</span>)  
  <span class="hljs-keyword">return</span>  
 }  
  
 describeNumber(number)  
}  
  
<span class="hljs-comment">// describeNumber prints the description of the number based on its value  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">describeNumber</span><span class="hljs-params">(number <span class="hljs-keyword">int</span>)</span></span> {  
 <span class="hljs-keyword">if</span> number%<span class="hljs-number">2</span> == <span class="hljs-number">0</span> {  
  describeEvenNumber(number)  
 } <span class="hljs-keyword">else</span> {  
  describeOddNumber(number)  
 }  
}  
  
<span class="hljs-comment">// describeEvenNumber handles even numbers  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">describeEvenNumber</span><span class="hljs-params">(number <span class="hljs-keyword">int</span>)</span></span> {  
 <span class="hljs-keyword">if</span> number &gt; <span class="hljs-number">10</span> {  
  fmt.Println(<span class="hljs-string">"The number is positive, even, and greater than 10."</span>)  
 } <span class="hljs-keyword">else</span> {  
  fmt.Println(<span class="hljs-string">"The number is positive, even, and 10 or less."</span>)  
 }  
}  
  
<span class="hljs-comment">// describeOddNumber handles odd numbers  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">describeOddNumber</span><span class="hljs-params">(number <span class="hljs-keyword">int</span>)</span></span> {  
 <span class="hljs-keyword">if</span> number &gt; <span class="hljs-number">10</span> {  
  fmt.Println(<span class="hljs-string">"The number is positive, odd, and greater than 10."</span>)  
 } <span class="hljs-keyword">else</span> {  
  fmt.Println(<span class="hljs-string">"The number is positive, odd, and 10 or less."</span>)  
 }  
}
</code></pre>
<h2 id="return-early-and-use-conditions-very-wisely">Return early and use conditions very wisely</h2>
<p>In programming  <strong>Return early</strong> is the practice where we need to return from the function as soon as we achieve the goal to avoid the unnecessary processing with the code and reduce the code complexity. It also helps to prevent deep nesting which I already explained in the above example.</p>
<p><strong>Example of bad practice</strong></p>
<pre><code class="hljsgo"><span class="hljs-keyword">package</span> main  
  
<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>  
  
<span class="hljs-comment">// checkTemperature categorizes the temperature into different ranges  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">checkTemperature</span><span class="hljs-params">(temp <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">string</span></span> {  
 <span class="hljs-keyword">if</span> temp &lt; <span class="hljs-number">0</span> {  
  <span class="hljs-keyword">return</span> <span class="hljs-string">"Freezing cold"</span>  
 } <span class="hljs-keyword">else</span> {  
  <span class="hljs-keyword">if</span> temp &gt;= <span class="hljs-number">0</span> {  
   <span class="hljs-keyword">if</span> temp &lt; <span class="hljs-number">15</span> {  
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Cold"</span>  
   } <span class="hljs-keyword">else</span> {  
    <span class="hljs-keyword">if</span> temp &gt;= <span class="hljs-number">15</span> &amp;&amp; temp &lt; <span class="hljs-number">25</span> {  
     <span class="hljs-keyword">return</span> <span class="hljs-string">"Mild"</span>  
    } <span class="hljs-keyword">else</span> {  
     <span class="hljs-keyword">if</span> temp &gt;= <span class="hljs-number">25</span> {  
      <span class="hljs-keyword">if</span> temp &lt; <span class="hljs-number">35</span> {  
       <span class="hljs-keyword">return</span> <span class="hljs-string">"Warm"</span>  
      } <span class="hljs-keyword">else</span> {  
       <span class="hljs-keyword">return</span> <span class="hljs-string">"Hot"</span>  
      }  
     } <span class="hljs-keyword">else</span> {  
      <span class="hljs-keyword">return</span> <span class="hljs-string">"Error"</span>  
     }  
    }  
   }  
  } <span class="hljs-keyword">else</span> {  
   <span class="hljs-keyword">return</span> <span class="hljs-string">"Error"</span>  
  }  
 }  
}  
  
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {  
 temp := <span class="hljs-number">22</span>  
 fmt.Println(<span class="hljs-string">"The weather is:"</span>, checkTemperature(temp))  
}
</code></pre>
<p><strong>Example of good practice</strong></p>
<pre><code class="hljsgo"><span class="hljs-keyword">package</span> main  
  
<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>  
  
<span class="hljs-comment">// checkTemperature categorizes the temperature into different ranges  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">checkTemperature</span><span class="hljs-params">(temp <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">string</span></span> {  
 <span class="hljs-keyword">if</span> temp &lt; <span class="hljs-number">0</span> {  
  <span class="hljs-keyword">return</span> <span class="hljs-string">"Freezing cold"</span>  
 }  
  
 <span class="hljs-keyword">if</span> temp &gt;= <span class="hljs-number">0</span> &amp;&amp; temp &lt; <span class="hljs-number">15</span> {  
  <span class="hljs-keyword">return</span> <span class="hljs-string">"Cold"</span>  
 }  
  
 <span class="hljs-keyword">if</span> temp &gt;= <span class="hljs-number">15</span> &amp;&amp; temp &lt; <span class="hljs-number">25</span> {  
  <span class="hljs-keyword">return</span> <span class="hljs-string">"Mild"</span>  
 }  
  
 <span class="hljs-keyword">if</span> temp &gt;= <span class="hljs-number">25</span> &amp;&amp; temp &lt; <span class="hljs-number">35</span> {  
  <span class="hljs-keyword">return</span> <span class="hljs-string">"Warm"</span>  
 }  
  
 <span class="hljs-keyword">return</span> <span class="hljs-string">"Hot"</span>  
}  
  
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {  
 temp := <span class="hljs-number">22</span>  
 fmt.Println(<span class="hljs-string">"The weather is:"</span>, checkTemperature(temp))  
}
</code></pre>
<h2 id="use-switch-case-more-often-instead-of-if-else">Use switch case more often instead of if/else</h2>
<p>The switch statement is the most effective way to reduce the length of functions in comparison with the if-else statement. The switch case statement  <strong>directly executes</strong>  the matching condition and exits the code to avoid unnecessary processing but the if-else statement checks all conditions until it matches with the one which causes unnecessary processing.</p>
<p>Switch statement also helps to improve the  <strong>code readability</strong>  and  <strong>reduce the code complexity</strong>. Switch cases provide  <strong>ease of maintenance,</strong>  <strong>performance optimization,</strong>  and  <strong>easy debugging</strong>.</p>
<p><strong>Example of bad practice</strong></p>
<pre><code class="hljsgo"><span class="hljs-keyword">package</span> main  
  
<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>  
  
<span class="hljs-comment">// categorizeTemperature categorizes the temperature using if-else  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">categorizeTemperature</span><span class="hljs-params">(temp <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">string</span></span> {  
 <span class="hljs-keyword">if</span> temp &lt; <span class="hljs-number">0</span> {  
  <span class="hljs-keyword">return</span> <span class="hljs-string">"Freezing cold"</span>  
 } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> temp &gt;= <span class="hljs-number">0</span> &amp;&amp; temp &lt; <span class="hljs-number">15</span> {  
  <span class="hljs-keyword">return</span> <span class="hljs-string">"Cold"</span>  
 } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> temp &gt;= <span class="hljs-number">15</span> &amp;&amp; temp &lt; <span class="hljs-number">25</span> {  
  <span class="hljs-keyword">return</span> <span class="hljs-string">"Mild"</span>  
 } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> temp &gt;= <span class="hljs-number">25</span> &amp;&amp; temp &lt; <span class="hljs-number">35</span> {  
  <span class="hljs-keyword">return</span> <span class="hljs-string">"Warm"</span>  
 } <span class="hljs-keyword">else</span> {  
  <span class="hljs-keyword">return</span> <span class="hljs-string">"Hot"</span>  
 }  
}  
  
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {  
 temp := <span class="hljs-number">22</span>  
 fmt.Println(<span class="hljs-string">"The weather is:"</span>, categorizeTemperature(temp))  
}
</code></pre>
<p><strong>Example of good practice</strong></p>
<pre><code class="hljsgo"><span class="hljs-keyword">package</span> main  
  
<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>  
  
<span class="hljs-comment">// categorizeTemperature categorizes the temperature using switch  </span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">categorizeTemperature</span><span class="hljs-params">(temp <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">string</span></span> {  
 <span class="hljs-keyword">switch</span> {  
 <span class="hljs-keyword">case</span> temp &lt; <span class="hljs-number">0</span>:  
  <span class="hljs-keyword">return</span> <span class="hljs-string">"Freezing cold"</span>  
 <span class="hljs-keyword">case</span> temp &gt;= <span class="hljs-number">0</span> &amp;&amp; temp &lt; <span class="hljs-number">15</span>:  
  <span class="hljs-keyword">return</span> <span class="hljs-string">"Cold"</span>  
 <span class="hljs-keyword">case</span> temp &gt;= <span class="hljs-number">15</span> &amp;&amp; temp &lt; <span class="hljs-number">25</span>:  
  <span class="hljs-keyword">return</span> <span class="hljs-string">"Mild"</span>  
 <span class="hljs-keyword">case</span> temp &gt;= <span class="hljs-number">25</span> &amp;&amp; temp &lt; <span class="hljs-number">35</span>:  
  <span class="hljs-keyword">return</span> <span class="hljs-string">"Warm"</span>  
 <span class="hljs-keyword">default</span>:  
  <span class="hljs-keyword">return</span> <span class="hljs-string">"Hot"</span>  
 }  
}  
  
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {  
 temp := <span class="hljs-number">22</span>  
 fmt.Println(<span class="hljs-string">"The weather is:"</span>, categorizeTemperature(temp))  
}
</code></pre>
<h2 id="continuous-code-refactoring">Continuous code refactoring</h2>
<p>It is crucial to periodically restructure the code in huge codebases and update it to reflect new developments. Golang is constantly changing it, thus it is necessary to update the codebase to the most recent version and apply the most recent modifications when needed. One of the most crucial aspects of code maintenance is the ability to maintain our code more modern and secure through refactoring.</p>
<h2 id="conclusion">Conclusion</h2>
<p>By following the small steps we can improve the code quality and maintainability of the code. There are small steps but very effective and I think most of them are not only stick to the golang, you can implement the same concepts in Java, Python, JavaScript, or any programming language, and it will be almost the same for others.</p>
<p>Thank you for reading this blog post, if you are interested in learning more about golang or full-stack development,  <strong><em>consider following me</em></strong>. I keep writing posts about it regularly.</p>
<ul>
<li><strong><em>Medium —</em></strong> <a href="https://medium.com/@harendra21"><strong><em>https://medium.com/@harendra21</em></strong></a></li>
<li><strong><em>Twitter (X) —</em></strong> <a href="https://x.com/harendraverma2"><strong><em>https://x.com/harendraverma2</em></strong></a></li>
</ul>
</p>]]></description>
                                                                    <author><![CDATA[Harendra Kumar]]></author>
                <guid>https://devdojo.com/12295</guid>
                <pubDate>Wed, 21 Aug 2024 02:13:37 -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[Livewire VS Inertia]]></title>
                <link>https://devdojo.com/tnylea/livewire-vs-inertia</link>
                <description><![CDATA[<p>When you start learning <a href="https://laravel.com" target="_blank">Laravel</a>, you will also need to choose a stack that suits your needs and preferences. <strong>Livewire</strong> or <strong>Inertia</strong>? Both offer unique benefits and can change how you build your apps. If you find it tough to decide, worry not! I'm here to break it down for you, so you can make an informed decision.</p>
<blockquote>
<p>Spoiler: they're both pretty awesome!</p>
</blockquote>
<p><a href="https://inertiajs.com/" target="_blank"><img src="https://cdn.devdojo.com/images/may2024/inertia.jpeg"></a></p>
<p>Think of <a href="https://inertiajs.com" target="_blank">Inertia</a> as the friendly matchmaker between Laravel and your favorite JavaScript library, whether it's React, Vue, or Svelte. It seamlessly passes data from your PHP to a JavaScript object/framework.</p>
<p><a href="https://livewire.laravel.com" target="_blank"><img src="https://cdn.devdojo.com/images/may2024/livewire.jpeg"></a></p>
<p><a href="https://livewire.laravel.com" target="_blank">Livewire</a> is like a wizard that lets you create SPA-like magic without diving too deep into JavaScript. It uses server-side rendering and offers real-time updates. Paired with it's companion <a href="https://alpinejs.dev" target="_blank">AlpineJS</a>, you have the power to create rich-interactive web apps.</p>
<h2 id="inertia-hits-the-spot">😎 Inertia Hits the Spot</h2>
<p>Most folks will reach for <a href="https://inertiajs.com" target="_blank">Inertia</a> because they're comfortable with a front-end framework like <a href="https://react.dev" target="_blank">React</a>, <a href="https://vuejs.org" target="_blank">Vue</a>, or <a href="https://svelte.dev" target="_blank">Svelte</a>. Inertia lets developers harness their existing powers and skills in these frameworks while seamlessly integrating with Laravel's powerful backend. Here are some reasons to choose Inertia:</p>
<ol>
<li>Seamless integration with React, Vue, or Svelte.</li>
<li>Enables SPA-like experiences with server-side routing.</li>
<li>Reuses front-end framework skills and components.</li>
<li>Simplifies data handling between Laravel and JavaScript.</li>
<li>Leverages modern JavaScript ecosystem and libraries.</li>
</ol>
<h2 id="the-livewire-magic">🧙‍♂️ The Livewire Magic</h2>
<p><a href="https://livewire.laravel.com" target="_blank">Livewire</a> is a game-changer. It offers a different approach to building dynamic SPA-like applications by keeping most of the logic on the server side. Pairing Livewire with its trusty sidekick <a href="https://alpinejs.dev" target="_blank">AlpineJS</a> gives you the ability to have a seamless conversation between the server and client. Here are some reasons you might want to reach for Livewire:</p>
<ol>
<li>Minimizes the need for JavaScript</li>
<li>Leverages server-side rendering for better SEO</li>
<li>Reduces complexity in state management</li>
<li>Integrates tightly with Laravel features</li>
<li>Facilitates rapid prototyping and development</li>
</ol>
<h2 id="downfalls">🙈 Downfalls</h2>
<p>No tech stack is without its quirks. With Inertia, you'll need to commit to a JavaScript framework, and state management can get pretty complex. On the flip side, Livewire might give you slower user interactions since it relies on server-side code. But hey, these downsides can be smoothed out with some careful planning and optimization.</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="which-should-you-choose">✅ Which Should You Choose</h2>
<p>In the end, the choice between Livewire and Inertia.js boils down to your project's specific needs and your team's expertise. Both are fantastic technologies designed to make your life easier and your apps more awesome.</p>
<h2 id="is-livewire-slow-and-bad-for-ux">🐌 Is Livewire Slow and bad for UX</h2>
<p>Some folks might say Livewire leads to a "not-so-great user experience." Nonsense! A poor UX can happen with any framework if not done right. In order to make the user experience better in Livewire you will use <a href="https://alpinejs.dev">AlpineJS</a> on the front-end. Use Alpine for quick, on-the-fly interactions and Livewire for all the heavy lifting on the backend.</p>
<h2 id="my-hot-take">🌶️ My Hot Take</h2>
<p>After working with Vue for a decade, I decided to dive-in head first with Livewire and I am loving it 😍 Easily passing data back and forth between the client and server has never been so enjoyable. If you haven't given Livewire a try yet, I highly recommend diving in – you might just fall in love with it too!</p>
<p>So, whether you go with Inertia or Livewire, remember: both paths lead to a more enjoyable and efficient developer experience 🤩</p>
]]></description>
                                                            <category>laravel</category>
                                            <category>livewire</category>
                                                    <author><![CDATA[Tony Lea]]></author>
                <guid>https://devdojo.com/12150</guid>
                <pubDate>Fri, 31 May 2024 07:24:04 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[A simple React app: fetch GitHub users information via API]]></title>
                <link>https://devdojo.com/baransel/a-simple-react-app-fetch-github-users-information-via-api</link>
                <description><![CDATA[<p>Welcome to a quick tutorial on my journey of building a React app that connects with external APIs. Today, I'm focusing on a hands-on example: fetching and displaying GitHub user data using GitHub's public API. This project is ideal for anyone looking to grasp API integration in React, whether you're starting out or brushing up your skills.</p>
<p>I'll walk you through setting up the React environment, exploring the GitHub API, and creating a user interface for displaying the data. Also, we'll look into using Axios for efficient API requests. By the end of this post, you'll see how I developed a functional React app that seamlessly integrates with an external API.</p>
<h2 id="setting-up-the-react-environment">Setting Up the React Environment</h2>
<p>Creating a React application is streamlined thanks to the tools and libraries in the React ecosystem. In this section, we'll cover the steps to set up a new React project, which is essential for our application to fetch GitHub user data.</p>
<h3 id="step-1-install-node-js-and-npm">Step 1: Install Node.js and npm</h3>
<p>Node.js and npm (Node Package Manager) are vital for managing the packages your React application will depend on. Here's how to install them on different operating systems:</p>
<ul>
<li>On Linux:</li>
</ul>
<p>Depending on your distribution, the installation commands may vary. For Debian and Ubuntu-based distributions, use:</p>
<pre><code class="hljs">sudo apt update
sudo apt install nodejs npm
</code></pre>
<ul>
<li>On macOS:</li>
</ul>
<p>You can install Node.js and npm using Homebrew, a package manager for macOS. If you haven't installed Homebrew, you can do so by running the following command in the terminal:</p>
<pre><code class="hljspowershell">/bin/bash <span class="hljs-literal">-c</span> <span class="hljs-string">"<span class="hljs-variable">$</span>(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"</span>
</code></pre>
<p>Once Homebrew is installed, install Node.js and npm by executing:</p>
<pre><code class="hljs">brew install node
</code></pre>
<ul>
<li>On Windows:</li>
</ul>
<p>Download the Node.js installer from the <a href="https://nodejs.org/en/download/">official website</a>. Once the download is complete, run the installer and follow the on-screen instructions.</p>
<h3 id="step-2-create-a-react-app">Step 2: Create a React App</h3>
<p>With Node.js and npm installed, create your React application by running:</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">npx create-react-app github-user-fetch
</code></pre>
<p>This command sets up a new project named <code>github-user-fetch</code>.</p>
<h3 id="step-3-navigate-to-your-project-directory">Step 3: Navigate to Your Project Directory</h3>
<p>Switch to your new project's directory:</p>
<pre><code class="hljs">cd github-user-fetch
</code></pre>
<h3 id="step-4-run-the-react-app">Step 4: Run the React App</h3>
<p>Start your React app locally with:</p>
<pre><code class="hljs">npm start
</code></pre>
<p>This opens the default React welcome page in your browser.</p>
<p><img src="https://baransel.dev/post/react-simple-github-fetch-user/react-welcome-page.png" alt="React welcome page"></p>
<h3 id="step-5-install-axios">Step 5: Install Axios</h3>
<p>Axios is a popular JavaScript library for making HTTP requests. We'll use it to fetch data from the GitHub API. Install Axios by running:</p>
<pre><code class="hljs">npm install axios
</code></pre>
<p>Your React application is now ready. Next, we'll delve into the GitHub API and begin crafting the user data fetching functionality.</p>
<h2 id="understanding-the-github-api">Understanding the GitHub API</h2>
<p>The GitHub API is a powerful tool that allows developers to interact programmatically with GitHub's vast array of functionalities. In our React app, we'll use this API to retrieve information about GitHub users.</p>
<h2 id="api-overview">API Overview</h2>
<p>GitHub provides a REST API that lets you fetch public information about users, repositories, issues, and more. For our app, the endpoint we'll be interested in is:</p>
<pre><code class="hljsgo">https:<span class="hljs-comment">//api.github.com/users/{username}</span>
</code></pre>
<p>Replace <code>{username}</code> with a GitHub username to fetch data for that specific user. This endpoint provides details like the user's name, number of repositories, followers, and more.</p>
<h3 id="rate-limiting">Rate Limiting</h3>
<p>It's important to note that GitHub's API has a rate limit. For unauthenticated requests, the limit is up to 60 requests per hour. This should be sufficient for our tutorial, but for larger applications, consider authenticating to increase the limit.</p>
<h3 id="fetching-user-data">Fetching User Data</h3>
<p>To fetch user data, we'll make a GET request to the API. The response will be in JSON format, providing us with the data we need to display in our app. In the next sections, we'll implement this in our React application using Axios, handle the response, and display the data.</p>
<h3 id="example-api-call">Example API Call</h3>
<p>Here’s a quick example of what an API call to GitHub looks like using <code>fetch</code> in JavaScript:</p>
<pre><code class="hljsjavascript">fetch(<span class="hljs-string">'https://api.github.com/users/kaex'</span>)
  .then(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> response.json())
  .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> <span class="hljs-built_in">console</span>.log(data))
  .catch(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error:'</span>, error));
</code></pre>
<p>This fetches data for the user <code>kaex</code>. We'll refine this process in our React app to make it dynamic and user-friendly.</p>
<h2 id="creating-the-user-interface">Creating the User Interface</h2>
<p>A user-friendly and intuitive interface is key to any application. For our React app, we'll design a simple yet functional UI that displays GitHub user information in a clear and engaging manner.</p>
<h3 id="ui-components">UI Components</h3>
<p>Our UI will consist of the following components:</p>
<ol>
<li><strong>Search Input</strong>: A text field where users can enter a GitHub username.</li>
<li><strong>Button</strong>: A button to trigger the search.</li>
<li><strong>User Information Display</strong>: An area to display the fetched user data.</li>
</ol>
<p>Let's start implementing these components in our React app:</p>
<h3 id="toc-1-search-input-and-submit-button">1) Search Input and Submit Button</h3>
<p>In <code>src/App.js</code>, add a text input and a button:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [username, setUsername] = useState(<span class="hljs-string">''</span>);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> 
        <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
        <span class="hljs-attr">value</span>=<span class="hljs-string">{username}</span>
        <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setUsername(e.target.value)}
        placeholder="Enter GitHub username"
      /&gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{fetchUserData}</span>&gt;</span>
        Fetch User
      <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>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Here, we use <code>useState</code> to manage the input state and an onChange handler to update it.</p>
<h3 id="toc-2-user-information-display">2) User Information Display</h3>
<p>We'll display the user information in a simple format. For now, let's create a placeholder where this data will be shown:</p>
<pre><code class="hljsjavascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...previous code...</span>

  <span class="hljs-keyword">const</span> [userData, setUserData] = useState(<span class="hljs-literal">null</span>);

  <span class="hljs-comment">// ...fetchUserData function will be added here...</span>

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      {/* Search input and button */}
      {/* ... */}
      
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        {userData &amp;&amp; (
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>User Information<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
            {/* Display user data here */}
          <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>We'll populate this section with real data in the next part, where we fetch and handle data from the GitHub API.</p>
<h3 id="styling-the-ui">Styling the UI</h3>
<p>Add some basic styling to <code>src/App.css</code> to make the UI more visually appealing. Here's a simple example:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">input</span> {
  <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span>;
}

<span class="hljs-selector-tag">button</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span> <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">cursor</span>: pointer;
}

<span class="hljs-selector-class">.user-info</span> {
  <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">20px</span>;
}
</code></pre>
<p>We now have a basic UI setup for our React application. Users can enter a username, trigger a search, and the app will display the fetched data in the designated area. In the next section, we'll integrate Axios to fetch data from the GitHub API and display it in our app.</p>
<h2 id="fetching-data-with-axios">Fetching Data with Axios</h2>
<p>Axios is a popular JavaScript library used for making HTTP requests. It simplifies the process of working with requests and responses in JavaScript, making it a great choice for our React app to interact with the GitHub API.</p>
<h3 id="why-axios-over-fetch">Why Axios Over Fetch</h3>
<p>While the native <code>fetch</code> API is capable, Axios provides several advantages:</p>
<ol>
<li><strong>Automatic JSON Data Transformation</strong>: Axios automatically transforms request and response data into JSON.</li>
<li><strong>Request and Response Interception</strong>: Offers the ability to intercept requests and responses to modify or log them.</li>
<li><strong>Error Handling</strong>: Easier error handling with a dedicated method for catching errors.</li>
</ol>
<h3 id="setting-up-axios">Setting Up Axios</h3>
<p>First, ensure Axios is installed in your project:</p>
<pre><code class="hljs">npm install axios
</code></pre>
<p>Next, let's integrate Axios into our React app to fetch GitHub user data.</p>
<h3 id="fetching-github-user-data">Fetching GitHub User Data</h3>
<p>In <code>src/App.js</code>, we'll add a function to fetch user data using Axios:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...previous state declarations...</span>

  <span class="hljs-keyword">const</span> fetchUserData = <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
    axios.get(<span class="hljs-string">`https://api.github.com/users/<span class="hljs-subst">${username}</span>`</span>)
      .then(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> {
        setUserData(response.data);
      })
      .catch(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error fetching data:'</span>, error);
        setUserData(<span class="hljs-literal">null</span>);
      });
  };

  <span class="hljs-comment">// ...UI rendering code...</span>
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>In this function, we make a GET request to the GitHub API for the entered username. The response is then stored in our <code>userData</code> state, ready to be displayed.</p>
<h3 id="error-handling">Error Handling</h3>
<p>Proper error handling is crucial. In the <code>.catch</code> block, we log the error and reset <code>userData</code> to <code>null</code>, ensuring our UI reacts appropriately in case of a failed request.</p>
<p>We've now integrated Axios into our React app for efficient API requests. Our app can fetch and handle data from GitHub, bringing us one step closer to displaying it. In the next section, we'll focus on mapping this fetched data to our UI components to display user information.</p>
<h2 id="displaying-user-data">Displaying User Data</h2>
<p>Now that we have successfully fetched user data from the GitHub API using Axios, the next step is to display this information in our application. We will map the data we've obtained to our UI components.</p>
<h3 id="mapping-data-to-ui-components">Mapping Data to UI Components</h3>
<p>In the <code>src/App.js</code> file, we already have a placeholder for user data. Let's enhance it to display the fetched data:</p>
<pre><code class="hljsjavascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...previous code...</span>

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      {/* Search input, button, and other UI elements */}
      {/* ... */}
      
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"user-info"</span>&gt;</span>
        {userData &amp;&amp; (
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>User Information<span class="hljs-tag">&lt;/<span class="hljs-name">h3</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">strong</span>&gt;</span>Username:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> {userData.login}<span class="hljs-tag">&lt;/<span class="hljs-name">p</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">strong</span>&gt;</span>Name:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> {userData.name}<span class="hljs-tag">&lt;/<span class="hljs-name">p</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">strong</span>&gt;</span>Followers:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> {userData.followers}<span class="hljs-tag">&lt;/<span class="hljs-name">p</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">strong</span>&gt;</span>Following:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> {userData.following}<span class="hljs-tag">&lt;/<span class="hljs-name">p</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">strong</span>&gt;</span>Repositories:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> {userData.public_repos}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            {/* Add more data fields as needed */}
          <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>In this snippet, we check if <code>userData</code> is available. If so, we display various pieces of user information like their username, name, followers, following count, and number of repositories.</p>
<h3 id="styling-the-display">Styling the Display</h3>
<p>For a better visual presentation, add some CSS to <code>src/App.css</code>:</p>
<pre><code class="hljscss"><span class="hljs-selector-class">.user-info</span> {
  <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ddd</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">15px</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
}

<span class="hljs-selector-class">.user-info</span> <span class="hljs-selector-tag">h3</span> {
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">10px</span>;
}

<span class="hljs-selector-class">.user-info</span> <span class="hljs-selector-tag">p</span> {
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">5px</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>This CSS will style the user information section, making it more readable and visually appealing.</p>
<h2 id="handling-no-data">Handling No Data</h2>
<p>It's important to handle cases where no data is available or an incorrect username is entered. You might want to display a message indicating that no user was found:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"user-info"</span>&gt;</span>
  {userData ? (
    {/* User data display code */}
  ) : (
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>No user data to display. Please search for a GitHub username.<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>
</code></pre>
<p>Our React app now not only fetches data from the GitHub API but also displays it in a user-friendly format. This completes the core functionality of our application. Users can enter a GitHub username, fetch relevant information, and view it immediately on the screen.</p>
]]></description>
                                                            <category>javascript</category>
                                            <category>api</category>
                                            <category>developer</category>
                                            <category>github</category>
                                            <category>js</category>
                                            <category>react</category>
                                            <category>react.js</category>
                                            <category>TypeScript</category>
                                            <category>beginner</category>
                                            <category>fetch</category>
                                            <category>ts</category>
                                                    <author><![CDATA[ ]]></author>
                <guid>https://devdojo.com/12076</guid>
                <pubDate>Wed, 17 Apr 2024 13:17:31 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Create an Interactive command line tool in Python 🐍]]></title>
                <link>https://devdojo.com/pratikpathak/create-an-interactive-command-line-tool-in-python</link>
                <description><![CDATA[<p>Creative an interactive in Python is very easy, there are many packages available that can help you to achieve this task. One such tool is Pyinquirer. Let’s learn about building a Python command line tool using Pyinquirer…</p>
<h2 id="let-s-start-building-the-project">Let’s start building the Project</h2>
<p>For Package Management🏢 we are going to use Poetry, Don’t know how to use poetry learn it from here <a href="https://pratikpathak.com/python-poetry-example/">https://pratikpathak.com/python-poetry-example/</a>.</p>
<p>We are going to build a simple command line tool, which gives an interactive menu of list of wifi passwords and when the user selects one option it will show the wifi password.</p>
<h2 id="create-a-commandline-python-project-by-using-poetry-python">Create a Commandline Python project by using Poetry Python</h2>
<p>Poetry can create a Boilerplate template for your project, you just need to use poetry new the command to create a new project. By using this command poetry will create all the necessary files required in a project.</p>
<pre><code class="hljsgo">poetry <span class="hljs-built_in">new</span> wifiPwd

</code></pre>
<p>This command will create a folder named wifiPwd and inside the folder, you will see a list of files, we will go through each of them individually.</p>
<p>The folder structure looks like this:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">D</span>:\<span class="hljs-selector-tag">PROJECTS</span>\<span class="hljs-selector-tag">WIFIPWD</span>  
│ <span class="hljs-selector-tag">pyproject</span><span class="hljs-selector-class">.toml</span>  
│ <span class="hljs-selector-tag">README</span><span class="hljs-selector-class">.md</span>  
│  
├───<span class="hljs-selector-tag">tests</span>  
│ __<span class="hljs-selector-tag">init__</span><span class="hljs-selector-class">.py</span>  
│  
└───<span class="hljs-selector-tag">wifipwd</span>  
        __<span class="hljs-selector-tag">init__</span><span class="hljs-selector-class">.py</span>

</code></pre>
<p>Tip! : You can use the “Tree” command to view the folder structure</p>
<h2 id="creating-the-driver-code-main-py">Creating the Driver Code main.py</h2>
<p>Let’s create a Python file named “main.py” in the root folder. The project entry point will be from here. Before writing the code let’s install all required packages beforehand.</p>
<p>We will use “<a href="https://pypi.org/project/PyInquirer/">PyInquirer</a>” to create an interactive terminal. let’s install PyInquirer using <code>poetry add</code> 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>
<h3 id="install-packages-using-poetry-add-command">Install Packages using Poetry Add Command</h3>
<pre><code class="hljs">poetry add pyinquirer

</code></pre>
<p>This command will fetch the PyPi registry, download the packages from here, and install it in your system. This will also update the pyproject.toml file, it will add Pyinquirer into dependencies.</p>
<p>Now we need a tool for formatting Python code. We will use the “Black” package for formatting our code. But this is not mandatory for the project, it’s a dev dependency, We want to format the code for us easily, it has no purpose in production which’s why it’s a dev dependency</p>
<h3 id="install-dev-package-using-poetry-add-command">Install Dev Package using Poetry Add Command</h3>
<pre><code class="hljs">poetry add black --group dev

</code></pre>
<p>First thing first, Step 0 of our program will be to run the command.</p>
<pre><code class="hljs">poetry install

</code></pre>
<p>This will validate the <code>pyproject.toml</code> file, and then it will install all the dependencies in the file. After running “poetry install” you will see output something like this</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">Creating</span> <span class="hljs-selector-tag">virtualenv</span> <span class="hljs-selector-tag">wifipwd-s1RAI5FV-py3</span><span class="hljs-selector-class">.9</span> <span class="hljs-selector-tag">in</span> <span class="hljs-selector-tag">C</span>:\<span class="hljs-selector-tag">Users</span>\<span class="hljs-selector-tag">pratik</span>\<span class="hljs-selector-tag">AppData</span>\<span class="hljs-selector-tag">Local</span>\<span class="hljs-selector-tag">pypoetry</span>\<span class="hljs-selector-tag">Cache</span>\<span class="hljs-selector-tag">virtualenvs</span>  
<span class="hljs-selector-tag">Resolving</span> <span class="hljs-selector-tag">dependencies</span>... (0<span class="hljs-selector-class">.1s</span>)

<span class="hljs-selector-tag">Installing</span> <span class="hljs-selector-tag">the</span> <span class="hljs-selector-tag">current</span> <span class="hljs-selector-tag">project</span>: <span class="hljs-selector-tag">wifipwd</span> (0<span class="hljs-selector-class">.1</span><span class="hljs-selector-class">.0</span>)

</code></pre>
<p>Did you notice? It says “Installing the current project: wifipwd (0.1.0)”, what does it mean?</p>
<p>It means Poetry is treating the whole project as a package. Now you can use <code>import wifipwd</code> it to import the project functionality directly.</p>
<p>Final pyproject.toml will look something like this –</p>
<pre><code class="hljsperl">[tool.poetry]  
name = <span class="hljs-string">"wifipwd"</span>  
version = <span class="hljs-string">"0.1.0"</span>  
description = <span class="hljs-string">""</span>  
authors = [<span class="hljs-string">"Pratik Pathak &lt;pratikpathak200@gmail.com&gt;"</span>]  
readme = <span class="hljs-string">"README.md"</span>

[tool.poetry.dependencies]  
python = <span class="hljs-string">"^3.9"</span>  
pyinquirer = <span class="hljs-string">"^1.0.3"</span>

[tool.poetry.group.dev.dependencies]  
black = <span class="hljs-string">"^23.12.1"</span>

[build-<span class="hljs-keyword">system</span>]  
requires = [<span class="hljs-string">"poetry-core"</span>]  
build-backend = <span class="hljs-string">"poetry.core.masonry.api"</span>


</code></pre>
<p>Now let’s go back to main.py file…</p>
<p>Here we have to take input from the user in our example wifi name, and then we will just show the output.</p>
<h3 id="how-to-interactively-take-input-from-commandline-python">How to Interactively take input from commandline python?</h3>
<p>You can easily create an interactive menu using Pyinquirer first lets get list all saved wifi password.</p>
<p>You can get the list of saved wifi passwords by using</p>
<pre><code class="hljsmarkdown">import subprocess

data = (  
<span class="hljs-code">    subprocess.check_output(["netsh", "wlan", "show", "profiles"])  </span>
<span class="hljs-code">    .decode("utf-8")  </span>
<span class="hljs-code">    .split("\n")  </span>
)  
profiles = [<span class="hljs-string">i.split(":")[1</span>][<span class="hljs-symbol">1:-1</span>] for i in data if "All User Profile" in i]

</code></pre>
<p>the list “profile” consist all the wifi names. Let’s go on and create an interactive menu using Pyinuirer</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">from</span> PyInquirer <span class="hljs-keyword">import</span> prompt

question = [  
    {  
        <span class="hljs-string">"type"</span>: <span class="hljs-string">"list"</span>,  
        <span class="hljs-string">"name"</span>: <span class="hljs-string">"wifi"</span>,  
        <span class="hljs-string">"message"</span>: <span class="hljs-string">"Choose the wifi whose password you want to see"</span>,  
        <span class="hljs-string">"choices"</span>: profiles,  
    }  
]  
answer = prompt(question)  
wifiName = answer[<span class="hljs-string">"wifi"</span>]

</code></pre>
<p>Now we have got the wifi name in “wifiName” variable, now the only thing remain is to create a function which returns the password.</p>
<p>The main.py file will look something like this</p>
<pre><code class="hljspython"><span class="hljs-keyword">import</span> subprocess  
<span class="hljs-keyword">from</span> PyInquirer <span class="hljs-keyword">import</span> prompt  
<span class="hljs-keyword">from</span> wifipwd <span class="hljs-keyword">import</span> wifiPassword

data = (  
    subprocess.check_output([<span class="hljs-string">"netsh"</span>, <span class="hljs-string">"wlan"</span>, <span class="hljs-string">"show"</span>, <span class="hljs-string">"profiles"</span>])  
    .decode(<span class="hljs-string">"utf-8"</span>)  
    .split(<span class="hljs-string">"\n"</span>)  
)  
profiles = [i.split(<span class="hljs-string">":"</span>)[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>:<span class="hljs-number">-1</span>] <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> data <span class="hljs-keyword">if</span> <span class="hljs-string">"All User Profile"</span> <span class="hljs-keyword">in</span> i]

question = [  
    {  
        <span class="hljs-string">"type"</span>: <span class="hljs-string">"list"</span>,  
        <span class="hljs-string">"name"</span>: <span class="hljs-string">"wifi"</span>,  
        <span class="hljs-string">"message"</span>: <span class="hljs-string">"Choose the wifi whose password you want to see"</span>,  
        <span class="hljs-string">"choices"</span>: profiles,  
    }  
]  
answer = prompt(question)  
wifiName = answer[<span class="hljs-string">"wifi"</span>]  
results = wifiPassword(wifiName)

<span class="hljs-keyword">try</span>:  
    print(<span class="hljs-string">"{:&lt;30}| \033[92m{:&lt;}\033[00m"</span>.format(wifiName, results))  
<span class="hljs-keyword">except</span> IndexError:  
    print(<span class="hljs-string">"{:&lt;30}| {:&lt;}"</span>.format(wifiName, <span class="hljs-string">""</span>))

</code></pre>
<p>Did you notice? we used “from wifipwd import wifiPassword”, we are treating the whole project like a package, so let’s write the “wifiPassword” function. wifiPassword function will be located at “wifipwd &gt; __init__.py”.</p>
<p>Now we will add wifiPassword() function, Lets edit “wifipwd &gt; __init__.py”, the final “__init__.py” will look like</p>
<pre><code class="hljspython"><span class="hljs-keyword">import</span> subprocess

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">wifiPassword</span><span class="hljs-params">(name)</span>:</span>  
    <span class="hljs-keyword">try</span>:  
        results = (  
            subprocess.check_output(  
                [<span class="hljs-string">"netsh"</span>, <span class="hljs-string">"wlan"</span>, <span class="hljs-string">"show"</span>, <span class="hljs-string">"profile"</span>, name, <span class="hljs-string">"key=clear"</span>]  
            )  
            .decode(<span class="hljs-string">"utf-8"</span>)  
            .split(<span class="hljs-string">"\n"</span>)  
        )  
    <span class="hljs-keyword">except</span> subprocess.CalledProcessError:  
        print(<span class="hljs-string">"{:&lt;30}| {:&lt;}"</span>.format(name, <span class="hljs-string">"ENCODING ERROR"</span>))

    results = [b.split(<span class="hljs-string">":"</span>)[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>:<span class="hljs-number">-1</span>] <span class="hljs-keyword">for</span> b <span class="hljs-keyword">in</span> results <span class="hljs-keyword">if</span> <span class="hljs-string">"Key Content"</span> <span class="hljs-keyword">in</span> b]  
    <span class="hljs-keyword">try</span>:  
        <span class="hljs-keyword">return</span> results[<span class="hljs-number">0</span>]  
    <span class="hljs-keyword">except</span> IndexError:  
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span>

</code></pre>
<p>The Project is Almost completed. The last step will be to format the code. We have already installed <code>Black</code> python package for code formatting, Let’s format our code using black.</p>
<h3 id="how-to-format-code-using-black-python-formatter">How to format code using Black Python formatter?</h3>
<p>It’s very easy to format our code using “Black” just run the below command.</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">black</span> <span class="hljs-selector-tag">main</span><span class="hljs-selector-class">.py</span>

</code></pre>
<p>Pro Tip!: You can also format the whole folder by providing the folder name for example.</p>
<pre><code class="hljs">black wifipwd

</code></pre>
<p>You will get output something like this</p>
<pre><code class="hljs">All done! ✨ 🍰 ✨  
1 file reformatted.

</code></pre>
<p>Note: If you get any error make sure you are inside a virtual environment, use “poetry shell” to activate the virtual environment, if it still not working update the black by using “poetry add black@latest”</p>
<h3 id="that-s-it">That’s it!</h3>
<p>You are done. You have successfully created a Python Command line tool, which is also packaged together using poetry.</p>
]]></description>
                                                                    <author><![CDATA[Pratik Pathak]]></author>
                <guid>https://devdojo.com/12073</guid>
                <pubDate>Mon, 15 Apr 2024 01:01:11 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Shifting from Laragon to Herd on Windows]]></title>
                <link>https://devdojo.com/bluepundit/shifting-from-laragon-to-herd-windows</link>
                <description><![CDATA[<p>This article compares <a href="https://laragon.org/index.html">Laragon</a> and <a href="https://herd.laravel.com/windows">Herd</a>, both excellent PHP development tools, and provides tips on transitioning between them.</p>
<p>Herd is a solid environment emphasizing ease of use over configurability. This design choice is both its biggest strength and weakness, but I’m pretty sure that a few more iterations (and releases) will bring it soon to a point of the perfect balance.</p>
<h2 id="dev-environments-are-handcrafted">Dev environments are handcrafted</h2>
<p>You probably know the feeling, setting up a local development environment to your preference can take several months and many rounds of tweaks. And, during a computer switch or reset, typically some of these improvements might be lost if they were not documented, making one hesitant to make changes.</p>
<p>Laragon has been one of my trusted <em>collaborators</em> for years.  Despite its imperfections, we've worked together long enough to understand each other's strengths and potential. I've found solutions or workarounds for all situations encountered so far, including multi-tenant apps with multiple local domains or adding new experimental PHP extensions.</p>
<p>I wasn't planning to switch environments, but curiosity about Herd led me to try it when I switched laptops.</p>
<h2 id="installing-laravel-herd">Installing Laravel Herd</h2>
<p>Starting with a clean slate allows for a straightforward setup of your new environment without having to worry about incompatibilities. It's a chance to embrace the capabilities of Herd, right off the bat.</p>
<p>Herd offers a <a href="https://herd.laravel.com/windows#plans">free and paid pro version</a>. While the free version is sufficient for basic developments, especially with Laravel 11 leaning towards Sqlite, it lacks support for MySql. This led me to choose the Pro plan, but know that you can also <a href="https://www.youtube.com/watch?v=9qVyrVNKYhQ">install MySql for free yourself</a>.</p>
<p>Installation is simple: You just download one file and run it. If needed, there are <a href="https://www.youtube.com/results?search_query=laravel+herd+installation">plenty of YouTube videos</a> explaining this. Unlike Laragon, Herd doesn't come with related tools, so I separately installed <a href="https://www.heidisql.com/">HeidiSQL</a> and <a href="https://cmder.app/">Cmder</a>, no need for the software bundled with Laragon. This resulted in a more minimal (and customizable) setup.</p>
<h2 id="adding-apps-to-herd">Adding apps to Herd</h2>
<p>Adding a project involves adding a directory to the Herd folder, which is customizable. I moved it to <code>D:\HERD\</code>. Like Laragon, Herd handles adding the domain to <code>etc/hosts</code> and creating the NGINX configuration. So an app in the <code> \mygreatapp</code> directory becomes immediately availabe on <em>mygreatapp.test</em>. Securing the domain (SSL) is as simple as clicking an icon and confirming (up to four times :-)).</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>When working on multi-domain sites (e.g. multi-tenant apps), a bit more flexibility is needed, e.g. to add or change configuration parameters. I couldn’t find (and would prefer) buttons for editing the <code>etc/host</code> manually, and accessing the sites-enabled directory. The same for a quick access button to the NGINX configuration file for each site. Small things, and not often needed, but having (and using) it with Laragon, makes me miss it here.</p>
<p>For the multi-domain basic use case, you can use the Herd CLI with the <code>herd link</code> command. Simply go to the directory for which you want an alias, <code>mygreatapp</code> for example, and type <code>herd link tenantdomain</code> to have the mygreatapp app also available from <em>tenantdomain.test</em>. Alternatively, you could create a softlink in the Herd path (<code>ln -s mygreatapp tenantdomain</code>), but as far as I can judge it's better to use the Herd CLI. And you can use <code>herd unlink</code> to remove an additional domain again.</p>
<p>If you need to make other changes, look for the configuration in <code>%USERPPROFILE%\.config\herd\config\valet\NginX</code>.</p>
<h2 id="built-in-services">Built-in services</h2>
<p>Herd includes additional services like a <strong>log viewer</strong>, <strong>dump viewer</strong>, and <strong>mail service</strong>. However, the mail service conflicts with others like HELO, by claiming port 2525 by default. I'd prefer to switch off the mail service or integrate with HELO. It would e.g. be enough for it to launch automatically when Herd starts, if selected by the user.</p>
<p>I'm also looking forward to trying <strong>xdebug</strong> soon. Until now, the complicated configuration deterred me, but Herd promises an easy setup. It needs however to be said, <a href="https://myray.app/">Laravel Ray</a> has been sufficient for most use cases, so I'm unsure about xdebug's added value (for me). And, while we are at it, Ray replaces the (added value of a) dump server, with a lot more functionalities. So a similar switch like with mail and HELO could be thinkable.</p>
<h2 id="transitioning-cookbook">Transitioning cookbook</h2>
<p>Transitioning is not as daunting as it seems. All I did was clone a git repository in my Herd folder. The subsequent steps (composer install, npm install, configuring the .env, setting a key, seeding dummy data, …) are the same as with Laragon. And don’t forget, of course, any other custom actions described in the <a href="http://readme.md/">readme.MD</a> file.</p>
<p>Connecting to the MySql database via HeidiSQL was smooth, and other tools like Laravel Ray worked out of the box. This means there's no need for a "cookbook" for switching to Herd.</p>
<p>And, lets be honest, switching is the ideal smoke test for your installation scripts or steps listed in the <a href="http://readme.md/">readme.MD</a> file of your repo.</p>
<h2 id="the-verdict">The verdict</h2>
<p>Laragon offers nearly unlimited configurability but at the cost of complexity. For example, switching to a new PHP version takes me about 30 minutes and requires consulting the documentation. In contrast, Herd simplifies this process to a single click, an addictive simplicity.</p>
<p>Laragon is free, while Herd costs 99 EUR. A free version of Herd exists, but without MySQL, it feels too limited. As a web development teacher, I'd love to see an academic version that has at least MySQL included for free. Or with all the services at a reduced price, and/or the possibility for monthly licenses.</p>
<p>Herd is stable and complete. It includes most of the functionalities that you will ever need. Power users may miss some direct access buttons to configuration files, and the built-in services could use a bit more flexibility or configurability.</p>
<p>Despite the higher price and need for some further refinement, I quickly adopted Herd as my new standard environment for local development. Like any new tool, there's a learning curve, but over time, your development process will become more streamlined and efficient, because you have to spend less even less time in configuring your tooling.</p>
]]></description>
                                                                    <author><![CDATA[ ]]></author>
                <guid>https://devdojo.com/12063</guid>
                <pubDate>Wed, 10 Apr 2024 04:58:59 -0700</pubDate>
            </item>
                    <item>
                <title><![CDATA[Ownership in Rust]]></title>
                <link>https://devdojo.com/francesco/ownership-in-rust</link>
                <description><![CDATA[<h2 id="ownership-in-rust">Ownership in Rust</h2>
<p>Rust has a concept of ownership that is unique among programming languages. It is a key feature of the language that allows it to be both safe and fast. In this lesson, we will explore what ownership is, how it works, and why it is important.</p>
<p>If you prefer a video version
<iframe width="100%" height="399" src="https://www.youtube.com/embed/9VBLOwmNE1g" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
<!-- <iframe width="905" height="510" src="https://www.youtube.com/embed/9VBLOwmNE1g" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> -->
<p>All the code is available on <a href="https://youtu.be/9VBLOwmNE1g">GitHub</a> (link available in the video description)</p>
<h2 id="in-this-lesson">In this lesson</h2>
<p>In this lesson, we will cover the following topics:</p>
<ul>
<li>What is ownership?</li>
<li>The <code>Stack</code> and the <code>Heap</code></li>
<li>Ownership rules</li>
<li>The <code>String</code> type</li>
<li>Memory allocation and the <code>Drop</code> function</li>
<li>The <code>Move</code> trait</li>
<li>The <code>Clone</code> trait</li>
<li>The <code>Copy</code> trait</li>
<li>Final Recap</li>
</ul>
<h2 id="what-is-ownership">What is Ownership?</h2>
<p><a href="https://youtu.be/9VBLOwmNE1g"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/28jojjsjaa5e91pidjc9.png" alt="Ownership in Rust - Rust programming tutorial"></a></p>
<p>Ownership is a distinct feature of Rust, enabling safe and efficient memory management.</p>
<p>In Rust, each value has a sole owner responsible for its disposal when it's not needed, eliminating the need for garbage collection or reference counting.</p>
<p>This lesson will be about ownership through examples centered on Strings, a common data structure.</p>
<p>⚠️ Before we proceed, it's important to understand that Ownership is not something that runs in the background or adds an overhead to the program. It's a concept that is enforced at compile time, and it's a key feature of the language that allows it to be both safe and fast.</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="the-stack-and-the-heap">The Stack and the Heap</h2>
<p>Understanding the stack and heap is essential in Rust due to its unique memory management through ownership.</p>
<h3 id="the-stack">The Stack</h3>
<p><a href="https://youtu.be/9VBLOwmNE1g"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mr8309obpcnxrppvccud.png" alt="The Stack and the Heap"></a></p>
<p>The Stack, used for fixed-size data, provides quick access.</p>
<p>It has some methods to manage the data, like <code>push</code> and <code>pop</code>, and it's very fast.</p>
<p>All the data stored on the stack must have a known, fixed size at compile time, making it ideal for small, fixed-size data.</p>
<p>Data with an unknown size or a size that might change at runtime is stored on the heap, which is slower than the stack due to its dynamic nature.</p>
<h3 id="the-heap">The Heap</h3>
<p>The Heap, on the other hand, is used for data whose size might change or cannot be determined until runtime.</p>
<p><a href="https://youtu.be/9VBLOwmNE1g"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ffqhlvd5glske35q1l2.png" alt="The Stack and the Heap"></a></p>
<p>The Heap is less organized than the stack, and it's slower to access data stored on it.</p>
<p>The momory allocated on the heap is not managed by the Rust compiler, but by the programmer. This is why Rust has a strict ownership model for managing the memory safely and efficiently, preventing memory leaks, and ensuring data is cleaned up properly.</p>
<p>Note: the pointer to the data is stored on the stack, but the data itself is stored on the heap.</p>
<h3 id="operations-on-the-stack-and-the-heap">Operations on the Stack and the Heap</h3>
<p>Here is a recap of the operations on the stack and the heap:</p>
<ul>
<li>
<p>The stack is fast and efficient, but it can only store data with a known, fixed size at compile time.</p>
</li>
<li>
<p>The heap is slower and less organized, but it can store data with an unknown size or a size that might change at runtime.</p>
</li>
<li>
<p>The memory allocated on the heap is not managed by the Rust compiler but by the programmer.</p>
</li>
<li>
<p>Functions like <code>push</code> and <code>pop</code> are available for the stack, but not for the heap.</p>
</li>
<li>
<p>The pointer to the data is stored on the stack, but the data itself is stored on the heap.</p>
</li>
</ul>
<p><a href="https://youtu.be/9VBLOwmNE1g"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b3s3080rmv5ffkbndnzi.png" alt="The Stack and the Heap"></a></p>
<h2 id="ownership-purpose-and-rules">Ownership purpose and  Rules</h2>
<p>Ownership's primary purpose is to manage the data stored on the Heap, ensuring it's cleaned up properly and preventing memory leaks.</p>
<p>To achieve this:</p>
<ul>
<li>it keeps track of what code is using what data on the heap</li>
<li>it minimizes the amount of duplicate data on the heap</li>
<li>it cleans up the data on the heap when it's no longer needed</li>
</ul>
<p>Rust has a few ownership rules:</p>
<ol>
<li>Each value in Rust has a variable that's its owner.</li>
<li>There can only be one owner at a time.</li>
<li>When the owner goes out of scope, the value will be dropped.</li>
</ol>
<p>Let's see an example.</p>
<h2 id="variable-scope">Variable scope</h2>
<p>In Rust, a variable is only valid within the scope it was declared.</p>
<p>Even when a variable is stored on the Stack, it is only valid within the scope it was declared.</p>
<pre><code class="hljsperl">fn main() {
{                      <span class="hljs-regexp">//</span> <span class="hljs-keyword">s</span> is <span class="hljs-keyword">not</span> valid here, it’<span class="hljs-keyword">s</span> <span class="hljs-keyword">not</span> yet declared
        let <span class="hljs-keyword">s</span> = <span class="hljs-string">"hello"</span>;   <span class="hljs-regexp">//</span> <span class="hljs-keyword">s</span> is valid from this point forward

        // <span class="hljs-keyword">do</span> stuff with <span class="hljs-keyword">s</span>
    }                      // this scope is now over, <span class="hljs-keyword">and</span> <span class="hljs-keyword">s</span> is <span class="hljs-keyword">no</span> longer valid
}
</code></pre>
<p>In the example above, <code>s</code> is the owner of the string "hello". When <code>s</code> goes out of scope, the string will not be valid anymore.</p>
<h2 id="the-string-type">The String type</h2>
<p>In a previous lesson, we covered simple, fixed-size data types in Rust.</p>
<p>Now, we'll explore the String type, a compound type allocated on the heap, with a detailed look planned for a future lesson.</p>
<p>We've touched on string literals, which are immutable and embedded in the program. Unlike these, the String type is mutable and heap-allocated, allowing it to hold text of variable length, unknown at compile time.</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">let</span> s = <span class="hljs-built_in">String</span>::<span class="hljs-keyword">from</span>(<span class="hljs-string">"hello"</span>);
</code></pre>
<p>This kind of string can be mutated:</p>
<pre><code class="hljsjavascript">    <span class="hljs-keyword">let</span> mut s = <span class="hljs-built_in">String</span>::<span class="hljs-keyword">from</span>(<span class="hljs-string">"hello"</span>);

    s.push_str(<span class="hljs-string">", world!"</span>); <span class="hljs-comment">// push_str() appends a literal to a String</span>

    println!(<span class="hljs-string">"{}"</span>, s); <span class="hljs-comment">// This will print `hello, world!`</span>
</code></pre>
<p>So, what’s the difference here?</p>
<p>The key difference lies in their memory handling: String can be mutated due to its dynamic memory allocation on the heap, while literals, stored in fixed memory, cannot be changed.</p>
<h2 id="memory-allocation-and-the-drop-function">Memory Allocation and the Drop Function</h2>
<pre><code class="hljsjavascript">    {
        <span class="hljs-keyword">let</span> s = <span class="hljs-built_in">String</span>::<span class="hljs-keyword">from</span>(<span class="hljs-string">"hello"</span>); <span class="hljs-comment">// s is valid from this point forward</span>

        <span class="hljs-comment">// do stuff with s</span>
    } <span class="hljs-comment">// this scope is now over, and s is no longer valid</span>
</code></pre>
<p>When a variable goes out of scope, Rust calls a special function for us.</p>
<p>This function is called <code>drop</code>, and it’s where the author of String can put the code to return the memory.</p>
<p>Rust calls drop automatically at the closing curly bracket.</p>
<h2 id="the-move-trait">The Move Trait</h2>
<p>If we try to do something like that:</p>
<pre><code class="hljsgo">    let s1 = <span class="hljs-number">5</span>;
    let s2 = s1;

    <span class="hljs-comment">// print s2</span>
    <span class="hljs-built_in">println</span>!(<span class="hljs-string">"{}, "</span>, s2);

    <span class="hljs-comment">// print s1</span>
    <span class="hljs-built_in">println</span>!(<span class="hljs-string">"{}, "</span>, s1);
</code></pre>
<p>This code is valid and it will print <code>5</code> twice.</p>
<p>If we do something like that:</p>
<pre><code class="hljsgo">    let s1 = <span class="hljs-string">"hello"</span>;
    let s2 = s1;

    <span class="hljs-comment">// print s2</span>
    <span class="hljs-built_in">println</span>!(<span class="hljs-string">"{}, "</span>, s2);

    <span class="hljs-comment">// print s1</span>
    <span class="hljs-built_in">println</span>!(<span class="hljs-string">"{}, "</span>, s1);
</code></pre>
<p>This code is also valid, and it will print <code>hello</code> twice.</p>
<p>But if we type something like that:</p>
<pre><code class="hljsjavascript">    <span class="hljs-keyword">let</span> s1 = <span class="hljs-built_in">String</span>::<span class="hljs-keyword">from</span>(<span class="hljs-string">"hello"</span>);
    <span class="hljs-keyword">let</span> s2 = s1;

    <span class="hljs-comment">// print s2</span>
    println!(<span class="hljs-string">"{}, "</span>, s2);

    <span class="hljs-comment">// print s1</span>
    println!(<span class="hljs-string">"{}, "</span>, s1);
</code></pre>
<p>This code will throw a compile-time error!</p>
<p>Why? Because Rust considers <code>s1</code> to be invalid after the assignment to <code>s2</code>. This is because Rust has a special trait called <code>Move</code> that is implemented for the <code>String</code> type.</p>
<p>Below is a schema of what happens. We might think that when we use the <code>=</code> operator, we copy the pointer or the whole data again, but this is not what happens: We <code>MOVE</code> the pointer to the new variable (that's why it's called Move trait).</p>
<p><a href="https://youtu.be/9VBLOwmNE1g"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/srnpcsspxgeqczuts5a9.png" alt="The Move Trait"></a></p>
<p>The <code>s1</code> variable is no longer valid after the assignment to <code>s2</code>.</p>
<p>To 'fix' our code, we can use the <code>clone</code> method (as suggested by the compiler):</p>
<pre><code class="hljsjavascript">    <span class="hljs-keyword">let</span> s1 = <span class="hljs-built_in">String</span>::<span class="hljs-keyword">from</span>(<span class="hljs-string">"hello"</span>);
    <span class="hljs-keyword">let</span> s2 = s1.clone();

    <span class="hljs-comment">// print s2</span>
    println!(<span class="hljs-string">"{}, "</span>, s2);

    <span class="hljs-comment">// print s1</span>
    println!(<span class="hljs-string">"{}, "</span>, s1);
</code></pre>
<h2 id="the-clone-trait">The Clone Trait</h2>
<p>If we call the clone method, what happens is exactly what is shown in the schema below: we copy the data and the pointer to the new variable.</p>
<p><a href="https://youtu.be/9VBLOwmNE1g"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zvij8e3m3bgt7lmmxykg.png" alt="The Clone Trait"></a></p>
<p>So why do  we need the clone? Because for the variables stored on the heap, Rust, by default, does not copy the data when we assign a variable to another. It only copies the pointer to the data. This is because copying the data would be expensive in terms of performance.</p>
<p>So if we use the <code>clone</code> method or we read someone else using the <code>clone</code> method in their code, we know that this was intentional and that the author of the code wanted to copy the data and the pointer to the new variable.</p>
<h2 id="stack-only-data-copy">Stack-Only Data: Copy</h2>
<p>So now you might be wondering: "Hey, if we need to use the <code>clone</code> method to copy the data and the pointer to the new variable, why didn't we need to use the <code>clone</code> method when we copied the integer or the string literal?"</p>
<pre><code class="hljsperl">    let <span class="hljs-keyword">x</span> = <span class="hljs-number">5</span>;
    let <span class="hljs-keyword">y</span> = <span class="hljs-keyword">x</span>;

    println!(<span class="hljs-string">"x = {}, y = {}"</span>, <span class="hljs-keyword">x</span>, <span class="hljs-keyword">y</span>);
</code></pre>
<p><a href="https://youtu.be/9VBLOwmNE1g"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6rdkmjp7shcs0gp3bsba.png" alt="The Copy Trait"></a></p>
<p>Integers, being simple fixed-size values, are stored directly on the stack, enabling Rust to copy their bits from one variable to another without invalidation upon scope exit. This is because there's no distinction between deep and shallow copying for such types, making bit-copy safe.</p>
<p>Rust utilizes the Copy trait for types like integers that reside on the stack, ensuring automatic data duplication without runtime overhead. However, types with the Drop trait, which require special cleanup, cannot implement Copy. Adding Copy to such types would lead to compile-time errors, as it contradicts the need for controlled resource release.</p>
<p>Types eligible for the Copy trait include simple scalar values that don't need dynamic allocation or aren't complex resources.</p>
<p>Examples include</p>
<ul>
<li>all integer types (e.g., u32)</li>
<li>the Boolean type (bool)</li>
<li>floating-point types (e.g., f64)</li>
<li>the character type (char)</li>
<li>tuples containing only Copy types (e.g., (i32, i32)).</li>
</ul>
<p>Conversely, tuples with non-Copy components, like (i32, String), do not implement Copy.</p>
<h2 id="final-recap">Final Recap</h2>
<p>In this lesson, we covered the following topics:</p>
<ul>
<li>What is ownership?</li>
<li>The <code>Stack</code> and the <code>Heap</code></li>
<li>Ownership rules</li>
<li>The <code>String</code> type</li>
<li>Memory allocation and the <code>Drop</code> function</li>
<li>The <code>Move</code> trait</li>
<li>The <code>Clone</code> trait</li>
<li>The <code>Copy</code> trait</li>
</ul>
<p>In the next lesson, we will cover references and borrowing in Rust.</p>
<p>If you prefer a video version
<iframe width="100%" height="399" src="https://www.youtube.com/embed/9VBLOwmNE1g" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
<!-- <iframe width="905" height="510" src="https://www.youtube.com/embed/9VBLOwmNE1g" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> -->
<p>All the code is available on <a href="https://youtu.be/9VBLOwmNE1g">GitHub</a> (link available in the video description)</p>
<p>You can find me here: <a href="https://francescociulla.com">Francesco</a></p>
]]></description>
                                                                    <author><![CDATA[Francesco Ciulla]]></author>
                <guid>https://devdojo.com/11963</guid>
                <pubDate>Wed, 21 Feb 2024 10:12:23 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[Understanding Testing in production]]></title>
                <link>https://devdojo.com/keploy/understanding-testing-in-production</link>
                <description><![CDATA[<p><img src="https://cdn.devdojo.com/images/february2024/Understanding%20Testing%20in%20production.png" alt="Understanding Testing in production.png"></p>
<p><strong>Introduction</strong></p>
<p><a href="https://keploy.io/blog/community/understanding-testing-in-production">Testing in production</a> was previously ignored by Product Developers, But recently it gaining Popularity Again! Even, more organizations are planning use this.</p>
<p>In this Article, We'll explore What is Testing in Production,How &amp; Why to use it,It's Benefits , drawbacks and many more.</p>
<p>So Without Delaying Further, Let's START!</p>
<p><strong>What is Testing in Production?</strong>
<img src="https://cdn.devdojo.com/images/february2024/pasted%20image%200.png" alt="pasted image 0.png">
Before we dive deep into this Topic, let's understand what is Testing in Production.</p>
<p>“Testing in production” refers to the practice of running code on production servers, using real data from real users, without showing the new behavior to the majority of users.</p>
<p>In other testing we used to do this by creating staging Environment. Creating a staging environment takes a long time, and the result may not match the actual product. Therefore, many web developers include testing in production as a complementary phase after pre-deployment examinations.</p>
<p>Note: Testing in production is not a substitute for quality assurance (QA), or a shortcut to eliminating unit testing or integration testing. Instead, it is an extension of testing and QA control points into the most realistic environment possible—the real-world.</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><strong>Why to use Test on Production?</strong></p>
<p>Because of it's unique advantages, Testing in Production is becoming an popular testing approach for Testers and QA Engineers.</p>
<p>Here are some reasons why one should start Testing on Production</p>
<p>The Most important point is, TiP works on real-world conditions, including user traffic, diverse data sets. This provide moe authentic Testing Environment than the stagging Environment.</p>
<p>It helps to detect issues in a live environment, which reduces the time between problem detection and implementation of solutions.</p>
<p>TiP provides immediate feedback, with which the Development team can fix emerging problems and make quick adjustments.</p>
<p>TiP utilizes real user data and traffic, providing accurate insights into actual application performance.</p>
<p>TiP can be more cost-effective compared to replicating complex production environments in pre-production stages.</p>
<p><strong>Drawbacks of testing in production</strong></p>
<p>While Testing in Production (TiP) offers various benefits, it comes with certain drawbacks which we should keep it mind before using it.</p>
<p><strong>Here are some Drawbacks of TiP:</strong></p>
<p>If a test fails or doesn't go according to plan, it can impact the production environment and potentially cause downtime.</p>
<p>Testing in Production may expose sensitive data and vulnerabilities, which can leave the system vulnerable to attacks.</p>
<p>If we have real-time payment processing, then it can be tricky as we can't perform actual transactions during testing.</p>
<p>Non-production tests often use mock data, but using this data in a production environment can impact the integrity of the production data.</p>
<p>Reproducing issues encountered in a dynamic live environment may be difficult, It makes debugging hard.</p>
<p><strong>Best Practices:</strong>
<img src="https://cdn.devdojo.com/images/february2024/unnamed2525252520225252525202.png" alt="unnamed2525252520225252525202.png">
To ensure successful testing in production, follow the points mentioned below:</p>
<p>1.<strong>Using Feature Flags:</strong> Feature flags allow testing of new features in production It also gives us the the ability to roll back changes if it's necessary. It reduces the risk of testing in the live environment.</p>
<p>2.<strong>Gradual Rollouts:</strong> Do changes gradually to a subset of users or servers, which will allow us to monitor carefully and identify issues before full deployment.</p>
<p>3.<strong>Automated Testing:</strong> Use automated testing processes to streamline the testing workflow. Automation ensures consistency, efficiency, and rapid identification of issues during testing in the production environment.</p>
<p>4.<strong>Monitoring and Observability:</strong> Use monitoring tools to track key performance metrics, errors, and user interactions in real-time. This provides valuable insights into the application's behavior during testing, facilitating quick issue resolution.</p>
<p>By following these best practices, development teams can enhance the effectiveness of Testing in Production.</p>
<p><strong>Conclusion</strong></p>
<p>If you found this blog post helpful, please consider sharing it with others who might benefit. You can also follow me for more content on Javascript, React, and other web development topics.</p>
]]></description>
                                                            <category>keploy</category>
                                            <category>testing in production</category>
                                                    <author><![CDATA[Keploy io]]></author>
                <guid>https://devdojo.com/11931</guid>
                <pubDate>Wed, 07 Feb 2024 22:43:49 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[Jsonnet Adventures: Deploying our application to ArgoCD!]]></title>
                <link>https://devdojo.com/kubeden/jsonnet-adventures-deploying-our-application-to-argocd</link>
                <description><![CDATA[<p>Hello fellow Kubernetes enthusiasts! In my previous blog posts we explored the basics of Jsonnet and how to use it to generate Kubernetes manifests. Now, I'm thrilled to finally get to the deployment of our production-ready, Jsonnet-templated application to ArgoCD.</p>
<h2 id="prerequisites">Prerequisites</h2>
<blockquote>
<p>I am expecting you to have a basic understanding of ArgoCD and Kubernetes for this one. If you need a refresher, check out some resources online (before I create my own tutorials on these topics):</p>
<ul>
<li><a href="https://argo-cd.readthedocs.io/en/stable/getting_started/">ArgoCD</a></li>
<li><a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-an-nginx-ingress-with-cert-manager-on-digitalocean-kubernetes">Nginx Ingress Controller &amp; Cert Manager</a></li>
</ul>
</blockquote>
<p>Before diving in, here's a quick overview of what you need to follow along:</p>
<ul>
<li>Read through <a href="https://dev.to/kubeden/introduction-to-jsonnet-the-yamljson-templating-language-n5g">Jsonnet Introduction</a></li>
<li>Read through <a href="https://dev.to/kubeden/jsonnet-adventures-functions-conditionals-and-advanced-templates-4cd3">Jsonnet Adventures: Functions, Conditionals, and Advanced Templates</a></li>
<li>Jsonnet Templates Repository: All the Jsonnet templates in those tutorials are stored in <a href="https://github.com/kubeden/tutorials">kubeden/tutorials</a> and you can use it as reference.</li>
<li>Application Repository: The source code for the Simple Node App is in <a href="https://github.com/kubeden/simple-nod-app">kubeden/simple-nod-app</a>. Yes, that is a typo lol</li>
<li>Docker Repository: The application's Docker image is available at <a href="https://hub.docker.com/repository/docker/kuberdenis/simple-node-app/general">kuberdenis/simple-node-app</a>.</li>
</ul>
<h2 id="argocd-and-jsonnet">ArgoCD and Jsonnet</h2>
<p>ArgoCD is a declarative, GitOps continuous delivery tool for Kubernetes. It allows us to deploy applications to Kubernetes using Git repositories as the source of truth. ArgoCD also supports Jsonnet, allowing us to deploy Jsonnet templates directly to Kubernetes.</p>
<p>I repeat - ArgoCD support Jsonnet out of the box! This is a huge advantage, as we can leverage Jsonnet's power to create dynamic Kubernetes manifests, and deploy them using ArgoCD.</p>
<h2 id="the-deployment-process">The Deployment Process</h2>
<ul>
<li>Jsonnet Templates: Using Jsonnet, I generated Kubernetes manifests that are both dynamic and maintainable.</li>
<li>ArgoCD Application: I created an application in ArgoCD pointing to the simple-node-app repository with a path to my <em>prd</em> directory. With a simple sync of the application, ArgoCD deployed the application to my Kubernetes cluster.</li>
</ul>
<h2 id="the-result">The Result</h2>
<p>The application, now live, dynamically greets users with the name provided in the YOUR_NAME environment variable.</p>
<p>Here is how the application looks like in ArgoCD:
<img src="https://cdn.devdojo.com/images/january2024/image.png" alt="image.png"></p>
<p>And here is the application itself:
<img src="https://cdn.devdojo.com/images/january2024/image-1.png" alt="image-1.png"></p>
<p>Now if we update the YOUR_NAME environment variable in the parameters.libsonnet file, and sync the application in ArgoCD, we can see the application dynamically update:
<img src="https://cdn.devdojo.com/images/january2024/image-2.png" alt="image-2.png"></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 small, yet powerful demonstration shows the flexibility and power of combining Jsonnet for templating Kubernetes for ArgoCD.</p>
<h2 id="conclusion">Conclusion</h2>
<p>If you made it this far, thank you for reading! I hope you enjoyed this series and learned something new. I am planning to continue writing about Jsonnet and other Kubernetes-related topics, so stay tuned!</p>
<p>For the next Jsonnet post, I am thinking of diving deep into combining multiple templating languages and use Jsonnet + Helm to deploy Grafana and Prometheus.</p>
<p>Thank you for following along and if you feel like chatting, feel free to hit me up on <a href="https://x.com/kubeden">x/twitter</a>!</p>
<p>And <em>drums</em> ... closing gif!
<img src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExOGdzbHM2ZjBjamxweW1udnp4ZGhvYzdhajNrOHVkdmRjOWJsNnF5bSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/Dh5q0sShxgp13DwrvG/giphy.gif" alt="gif-link"></p>
]]></description>
                                                                    <author><![CDATA[ ]]></author>
                <guid>https://devdojo.com/11918</guid>
                <pubDate>Wed, 31 Jan 2024 11:03:26 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to set up your own proxy server in minutes?]]></title>
                <link>https://devdojo.com/dhairyashah/how-to-set-up-your-own-proxy-server-in-minutes</link>
                <description><![CDATA[<p>Today, most websites track your online activities by capturing essential information such as IP addresses, cookies, and geolocation. All this information starts associating with the user, and a unique user profile is created over time. This helps the trackers to track the user’s activities over the internet, which in the end results in privacy concerns.</p>
<p>Unlike third-party cookies, there is no straightforward way to opt out of this kind of tracking.</p>
<h2 id="what-is-a-proxy-server">What is a Proxy Server?</h2>
<p>A proxy server is an intermediate server that acts as a gateway between the user’s device and the internet. When a user makes a request for a resource, let’s say a website, the request is first sent to the proxy server. The proxy server then forwards the request to the website’s server on behalf of the user.</p>
<p><img src="https://res.cloudinary.com/dtscloud/image/upload/v1700734309/Post%20Images/proxy-server-1.png" alt="Untitled"></p>
<h3 id="benefits-of-using-a-proxy-server">Benefits of using a Proxy Server</h3>
<ul>
<li><strong>Privacy Protection:</strong> Proxy servers can be used to protect the user’s privacy. They are used to hide the user’s actual IP address from the websites they visit.</li>
<li><strong>Improved Performance:</strong> Proxy servers can be used to cache the requests that are frequently accessed. This helps in faster response and reduces the bandwidth usage.</li>
<li><strong>Optimal Performance:</strong> In a server having large traffic (requests), proxy servers can be used for load balancing. Requests from the users are distributed across multiple servers to provide optimal performance and prevent the single server from being overloaded by the huge traffic.</li>
<li><strong>Enhanced Security:</strong> As we discussed earlier, the proxy server provides an additional layer of security by acting as the middleman between the user’s device and the internet. They can filter out the malicious content and block certain types of attacks.</li>
</ul>
<p>Proxy servers can be operated at different OSI models, including the HTTP proxies, transport proxies (SOCKS proxies), or even the lower layers.</p>
<h2 id="so-how-to-make-our-proxy-server">So, how to make our proxy server?</h2>
<h3 id="prerequisites">Prerequisites</h3>
<ul>
<li>Any cloud service provider account. (DigitalOcean Recommended)</li>
<li>One Ubuntu 20.04 or later server, with at least 512MB of RAM.</li>
</ul>
<h3 id="create-an-ubuntu-server">Create an Ubuntu Server</h3>
<p>In this tutorial, I will be using DigitalOcean to host my VPS.</p>
<p>Create an account on DigitalOcean, if you haven’t done so yet.</p>
<p><a href="https://m.do.co/c/59c3a25113a5">Use my referral link to create an account on DigitalOcean</a> to get $200 worth of credits for 60 days.</p>
<p>Now create a new 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>
<p>In the sidebar click <strong>New Project</strong>:</p>
<p><img src="https://res.cloudinary.com/dtscloud/image/upload/v1700734234/Post%20Images/proxy-server-2.jpg" alt="Create New Project"></p>
<p>Enter a name, for example, “<strong>Proxy Server”</strong>.</p>
<p>Choose something in the “Tell us what it’s for” box, or you can’t create the project. Then click <strong>Create Project</strong>.</p>
<p>In the next page, click “Skip for now”</p>
<p><img src="https://res.cloudinary.com/dtscloud/image/upload/v1700734234/Post%20Images/proxy-server-3.jpg" alt="Skip for now"></p>
<p>The project has now been created.</p>
<p>Now, click on <strong>Spin up a Droplet</strong> to create a VPS (Virtual Private Server).</p>
<p><img src="https://res.cloudinary.com/dtscloud/image/upload/v1700734234/Post%20Images/proxy-server-4.jpg" alt="Spin up a droplet"></p>
<p>This shows the interface for creating a new droplet.</p>
<p>Choose a region where you want your internet activity to be routed through the proxy.</p>
<p><img src="https://res.cloudinary.com/dtscloud/image/upload/v1700734234/Post%20Images/proxy-server-5.jpg" alt="Choose a region"></p>
<p>To initiate the installation of Ubuntu on your Droplet, simply pick the Ubuntu option from the 'Choose an image' section.</p>
<p>Now, choose the droplet size. The default selection is the 'Premium Intel/AMD' CPUs, but for our current needs, we don't require that level of power. Instead, opt for the 'Regular' CPU option, which comes with a $4 per month plan. Note that in certain regions, the $4 per month plan may not be accessible, so in such cases, pick the most economical plan offered in that region.</p>
<p><img src="https://res.cloudinary.com/dtscloud/image/upload/v1700734234/Post%20Images/proxy-server-6.jpg" alt="Choose the droplet size"></p>
<p>Now go down below, and set up a strong root user password.</p>
<p><img src="https://res.cloudinary.com/dtscloud/image/upload/v1700734234/Post%20Images/proxy-server-7.jpg" alt="Create strong root password"></p>
<p>Finally, create the <strong>Droplet</strong>.</p>
<p>It may take some time to boot the server. Once the server starts running, copy the IPv4 address.</p>
<p><img src="https://res.cloudinary.com/dtscloud/image/upload/v1700734234/Post%20Images/proxy-server-8.jpg" alt="Copy to ipv4 address"></p>
<p>Now, we have successfully configured our server setup.</p>
<h3 id="configuring-the-server">Configuring the Server</h3>
<p>Open the terminal, and login to your Ubuntu server using <code>openssh</code>.</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">ssh</span> <span class="hljs-selector-tag">root</span><span class="hljs-keyword">@YOUR_SERVER_IPV4</span>
</code></pre>
<p>After you log into your Ubuntu server. Install <code>openssh-server</code>.</p>
<pre><code class="hljs">apt install openssh-server
</code></pre>
<p>Once you have finished installing, log out from the server.</p>
<pre><code class="hljspowershell"><span class="hljs-keyword">exit</span>
</code></pre>
<h3 id="setting-up-the-proxy-locally">Setting up the proxy locally</h3>
<p>On your local machine, open the terminal and run the following command,</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">ssh</span> <span class="hljs-selector-tag">-D</span> 2402 <span class="hljs-selector-tag">-N</span>  <span class="hljs-selector-tag">-C</span> <span class="hljs-selector-tag">root</span><span class="hljs-keyword">@YOUR_SERVER_IPV4</span>
</code></pre>
<p>Now open the Firefox browser, and open the settings. In settings, search for the “Proxy”.</p>
<p><img src="https://res.cloudinary.com/dtscloud/image/upload/v1700734234/Post%20Images/proxy-server-9.jpg" alt="Firefox > settings> proxy"></p>
<p>Now click on the “Settings”,</p>
<p><img src="https://res.cloudinary.com/dtscloud/image/upload/v1700734234/Post%20Images/proxy-server-10.jpg" alt="Manual Proxy Configuration"></p>
<p>Choose, the “Manual Proxy Configuration”, and enter the details in the <strong>SOCKS Host</strong> input field as shown in the above image.</p>
<p><code>localhost</code> | <code>PORT: 2402</code></p>
<p>Now click on OK, and you have successfully configured the proxy.</p>
<h3 id="testing-the-proxy-server">Testing the proxy server</h3>
<p>Before connecting to the proxy server:</p>
<p><img src="https://res.cloudinary.com/dtscloud/image/upload/v1700734234/Post%20Images/proxy-server-11.jpg" alt="Before connecting to proxy server"></p>
<p>After connecting to the proxy server:</p>
<p><img src="https://res.cloudinary.com/dtscloud/image/upload/v1700734234/Post%20Images/proxy-server-12.jpg" alt="After connecting to proxy server"></p>
<h3 id="conclusion">Conclusion</h3>
<p>So, this is how we can set up a proxy server on our own, and protect our privacy, and maintain anonymity online.</p>
<p>I hope this article, was helpful to you. Thanks for reading!</p>
<p><a href="https://twitter.com/dhairyashah_dev/">Follow me on X</a></p>
]]></description>
                                                            <category>javascript</category>
                                            <category> DigitalOcean</category>
                                            <category>web</category>
                                            <category>cloud</category>
                                            <category>tutorial</category>
                                            <category>aws</category>
                                                    <author><![CDATA[ ]]></author>
                <guid>https://devdojo.com/11911</guid>
                <pubDate>Mon, 29 Jan 2024 20:58:57 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[Variables, Shadowing, and Constants in Rust]]></title>
                <link>https://devdojo.com/francesco/variables-shadowing-and-constants-in-rust</link>
                <description><![CDATA[<h2 id="understanding-variables-in-rust">Understanding Variables in Rust</h2>
<p>Rust is a statically and strongly typed language. This means that the compiler must know the type of all variables at compile time. The compiler can usually infer what type we want to use based on the value and how we use it. In cases when many types are possible, we must add a type annotation.</p>
<p>In this article, we will learn about the following:</p>
<ul>
<li>variables and mutability</li>
<li>shadowing</li>
<li>constants</li>
</ul>
<p>If you prefer a video version
<iframe width="100%" height="399" src="https://www.youtube.com/embed/6Ag0MZUlvBE" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
<!-- <iframe width="905" height="510" src="https://www.youtube.com/embed/6Ag0MZUlvBE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> -->
<h2 id="variables-and-mutability">Variables and mutability</h2>
<p>Let's start with understanding variables and mutability.</p>
<p>Let's declare a variable and print it:</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-number">5</span>;
    println!(<span class="hljs-string">"x is {}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>Here, we are using string interpolation to print the value of x. The curly braces are placeholders for the value of x. The value of x is then passed as an argument to println!().</p>
<p>You should see the following output:</p>
<p><a href="https://youtu.be/6Ag0MZUlvBE"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8qb73ju8ggyqerceqayn.png" alt="Variables in Rust - Rust programming tutorial"></a></p>
<p>This is an implicit declaration of type, but at compile time, Rust infers the type of x to be i32. This is because we assigned the value 3 to x, and integer literals default to i32 unless otherwise specified.</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 want to declare the variable type explicitly, you can use a type annotation, which is the colon (:) followed by a type name.</p>
<p>Let's try something that seems we can do but we actually can't:</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-number">5</span>;
    println!(<span class="hljs-string">"x is {}"</span>, <span class="hljs-keyword">x</span>);
    
    <span class="hljs-keyword">x</span> = <span class="hljs-number">6</span>;
    println!(<span class="hljs-string">"x is {}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>In Rust, variables are immutable by default. This means that once we assign a value to a variable, we can't change it. If we try to change the value of a variable, we get an error:</p>
<p><a href="https://youtu.be/6Ag0MZUlvBE"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2vhngye6mxl6p7lzdyv9.png" alt="Variables in Rust - Rust programming tutorial"></a></p>
<h3 id="the-mut-keyword">the mut keyword</h3>
<p>If we want to change a variable's value, we must declare it as mutable. We do this by adding the <code>mut</code> keyword before the variable name, as shown here:</p>
<pre><code class="hljsperl">fn main() {
    let mut <span class="hljs-keyword">x</span> = <span class="hljs-number">5</span>;
    println!(<span class="hljs-string">"x is {}"</span>, <span class="hljs-keyword">x</span>);
    
    <span class="hljs-keyword">x</span> = <span class="hljs-number">6</span>;
    println!(<span class="hljs-string">"x is {}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>Now we can change the value of x without getting an error.</p>
<h2 id="shadowing">Shadowing</h2>
<p>Now, let's see the concept of shadowing.</p>
<p>We can change the value of an immutable variable by shadowing it. Shadowing is when we declare a new variable with the same name as a previous variable. The new variable shadows the previous variable, and we can assign a new value to the new variable while the old variable remains unchanged.</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-number">5</span>;
    println!(<span class="hljs-string">"x is {}"</span>, <span class="hljs-keyword">x</span>);
    
    let <span class="hljs-keyword">x</span> = <span class="hljs-number">6</span>;
    println!(<span class="hljs-string">"x is {}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>You might think that we would get an error here, but we don't. This is because we are declaring a new variable, not changing the value of the old one. The old variable is still there, but the new variable shadows it. We can't use the old variable anymore, but we can use the new one.</p>
<p><a href="https://youtu.be/6Ag0MZUlvBE"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4zzusn0tp83563m2nxa9.png" alt="Variables in Rust - Rust programming tutorial"></a></p>
<p>I can even use the previous variable's value to initialize the new variable:</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-number">5</span>;
    println!(<span class="hljs-string">"x is {}"</span>, <span class="hljs-keyword">x</span>);
    
    let <span class="hljs-keyword">x</span> = <span class="hljs-keyword">x</span> + <span class="hljs-number">2</span>;
    println!(<span class="hljs-string">"x is {}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p><a href="https://youtu.be/6Ag0MZUlvBE"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/un14a3em4wjw7bps0igj.png" alt="Variables in Rust - Rust programming tutorial"></a></p>
<h3 id="name-shadowing-in-a-different-scope">Name shadowing in a different scope</h3>
<p>Name shadowing is when we declare a new variable with the same name as a previous variable. The new variable shadows the previous variable, and we can assign a new value to the new variable while the old variable remains unchanged.</p>
<p>Let's see an example:</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-number">5</span>;
    println!(<span class="hljs-string">"x is {}"</span>, <span class="hljs-keyword">x</span>);

    {
        let <span class="hljs-keyword">x</span> = <span class="hljs-number">8</span>;
        println!(<span class="hljs-string">"x is {}"</span>, <span class="hljs-keyword">x</span>);
    }

    let <span class="hljs-keyword">x</span> = <span class="hljs-keyword">x</span> + <span class="hljs-number">2</span>;
    println!(<span class="hljs-string">"x is {}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p><a href="https://youtu.be/6Ag0MZUlvBE"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mfzoua8r1p0crmuerhio.png" alt="Variables in Rust - Rust programming tutorial"></a></p>
<h3 id="changing-the-type">Changing the type</h3>
<p>When you use shadowing, you can change the type of a variable. This is because you are declaring a new variable, not changing the value of the old one.</p>
<p>You can use it to change the type of a variable:</p>
<pre><code class="hljsperl">fn main() {
    let <span class="hljs-keyword">x</span> = <span class="hljs-number">5</span>;
    println!(<span class="hljs-string">"x is {}"</span>, <span class="hljs-keyword">x</span>);

    let <span class="hljs-keyword">x</span> = <span class="hljs-string">"hello world!"</span>;
    println!(<span class="hljs-string">"x is {}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p><a href="https://youtu.be/6Ag0MZUlvBE"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xuqcnkw34hfuokqppr7y.png" alt="Variables in Rust - Rust programming tutorial"></a></p>
<p>But you can't change the type of a variable without shadowing it:</p>
<pre><code class="hljsperl">fn main() {
    let mut <span class="hljs-keyword">x</span> = <span class="hljs-number">5</span>;
    println!(<span class="hljs-string">"x is {}"</span>, <span class="hljs-keyword">x</span>);

    <span class="hljs-keyword">x</span> = <span class="hljs-string">"hello world!"</span>;
    println!(<span class="hljs-string">"x is {}"</span>, <span class="hljs-keyword">x</span>);
}
</code></pre>
<p>We will get an error.</p>
<p><a href="https://youtu.be/6Ag0MZUlvBE"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k24vlf1k6w521qu9nmhj.png" alt="Variables in Rust - Rust programming tutorial"></a></p>
<p>This means that if we use shadowing, we can change the type, but if a variable is mutable, we can't change the type without shadowing it.</p>
<h2 id="constants-example">Constants Example</h2>
<p>Constants are variables that are immutable and have a fixed value.</p>
<p>They are declared using the `const`` keyword. They must be annotated with a type, and they can only be set to a constant expression, not the result of a function call or any other value that could only be computed at runtime.</p>
<pre><code class="hljsgo">fn main() {
    <span class="hljs-keyword">const</span> MAX_LEVEL:i32 = <span class="hljs-number">100</span>_000;
    <span class="hljs-built_in">println</span>!(<span class="hljs-string">"The maximum level is: {}"</span>, MAX_LEVEL);
}
</code></pre>
<p>Recapt for constants:</p>
<ul>
<li>you must declare the type of the value</li>
<li>you must assign a value to the constant on the declaration</li>
<li>you cannot redefine or mutate a constant (shadowing doesn't work, can't use <code>mut</code> keyword, can't reassign)</li>
</ul>
<p>This ends the article.</p>
<p>I hope you enjoyed it and learned something new. If you have any questions, feel free to leave a comment below.</p>
<p>If you prefer a video version
<iframe width="100%" height="399" src="https://www.youtube.com/embed/6Ag0MZUlvBE" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
<!-- <iframe width="905" height="510" src="https://www.youtube.com/embed/6Ag0MZUlvBE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> -->
<p>ALL the links here: https://francescociulla.com</p>
]]></description>
                                                                    <author><![CDATA[Francesco Ciulla]]></author>
                <guid>https://devdojo.com/11884</guid>
                <pubDate>Tue, 16 Jan 2024 09:49:35 -0800</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[Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma, Postgres, Docker]]></title>
                <link>https://devdojo.com/francesco/build-a-full-stack-web-app-with-javascript-api-nextjs-14-nodejs-express-prisma-postgres-docker</link>
                <description><![CDATA[<p>By the end of this article, you will understand and  create a simple yet complete full stack app using the following:</p>
<ul>
<li>Next.js 14 (TypeScript)</li>
<li>Tailwind CSS</li>
<li>Node.js</li>
<li>Express (JavaScript)</li>
<li>Prisma</li>
<li>PostgreSQL</li>
<li>Docker</li>
<li>Docker Compose</li>
</ul>
<p>There are MANY technologies, but we'll keep the example as basic as possible to make it understandable.</p>
<p>We will proceed with a bottom-up approach, starting with the database and ending with the frontend.</p>
<p>If you prefer a video version
<iframe width="100%" height="399" src="https://www.youtube.com/embed/N-7uYm1PszM" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
<!-- <iframe width="905" height="510" src="https://www.youtube.com/embed/N-7uYm1PszM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> -->
<p>All the code is available for free on <a href="https://youtu.be/N-7uYm1PszM">GitHub</a> (link in video description).</p>
<h2 id="architecture">Architecture</h2>
<p>Before we start, here is a simple schema explaining the app's architecture.</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8u5tuvomas4zw37m4g5z.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>The frontend is a Next.js app with TypeScript and Tailwind CSS. The backend is a Node.js app, with Express and Prisma as ORM. The database is PostgreSQL. We will use Docker to run the database, the backend, and also the frontend (you can also use Vercel). We will use Docker Compose to run the frontend, the backend, and the database together.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>Basic knowledge of what is a frontend, a backend, an API, and a database</li>
<li>Docker installed on your machine</li>
<li>Node.js installed on your machine</li>
<li>(optional) Postman or any other tool to make HTTP requests</li>
</ul>
<h2 id="toc-1-preparation">1. Preparation</h2>
<p>Create any folder you want, and then open it with your favorite code editor.</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">mkdir <span class="hljs-tag">&lt;<span class="hljs-name">YOUR_FOLDER</span>&gt;</span>
cd <span class="hljs-tag">&lt;<span class="hljs-name">YOUR_FOLDER</span>&gt;</span>
code .
</code></pre>
<p>Initialize a git repository.</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">git</span> <span class="hljs-selector-tag">init</span>
<span class="hljs-selector-tag">touch</span> <span class="hljs-selector-class">.gitignore</span>
</code></pre>
<p>Populate the <code>.gitignore</code> file with the following content:</p>
<pre><code class="hljs">*node_modules
</code></pre>
<p>Create a file called <code>compose.yaml</code> in the project's root.</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">touch</span> <span class="hljs-selector-tag">compose</span><span class="hljs-selector-class">.yaml</span>
</code></pre>
<p>Your projects should look like this:</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iq8f76ipe6oqpbppexg9.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>We are ready to create the fullstack app and build it from the bottom up, starting with the database.</p>
<p>After each step, we will test the app's current state to ensure that everything is working as expected.</p>
<h2 id="toc-2-database">2. Database</h2>
<p>We will use Postgres but not install it on our machine. Instead, we will use Docker to run it in a container. This way, we can easily start and stop the database without installing it on our machine.</p>
<p>Open the file <code>compose.yaml</code> and add the following content:</p>
<pre><code class="hljsmarkdown">version: '3.9'

services:
  db:
<span class="hljs-code">    container_name: db</span>
<span class="hljs-code">    image: postgres:12</span>
<span class="hljs-code">    restart: always</span>
<span class="hljs-code">    environment:</span>
<span class="hljs-code">      POSTGRES_USER: postgres</span>
<span class="hljs-code">      POSTGRES_PASSWORD: postgres</span>
<span class="hljs-code">      POSTGRES_DB: postgres</span>
<span class="hljs-code">    ports:</span>
<span class="hljs-bullet">      - </span>5432:5432
<span class="hljs-code">    volumes:</span>
<span class="hljs-bullet">      - </span>pgdata:/var/lib/postgresql/data

volumes:
  pgdata: {}

</code></pre>
<p>then type in your terminal</p>
<pre><code class="hljs">docker compose up -d
</code></pre>
<p>This will pull the Postgres image from Docker Hub and start the container. The <code>-d</code> flag means that the container will run in detached mode so we can continue to use the terminal.</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0694qmlj0fr5dg9splw6.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>Check if the container is running:</p>
<pre><code class="hljs">docker ps -a
</code></pre>
<p>Step into the db container</p>
<pre><code class="hljspython">docker <span class="hljs-keyword">exec</span> -it db psql -U postgres
</code></pre>
<p>Now that you are in the Postgres container, you can type:</p>
<pre><code class="hljs">\l
</code></pre>
<pre><code class="hljs">\dt
</code></pre>
<p>And you should see no relations.</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ux11thp61ytcp2v3d0k.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>You can now exit the container with the <code>exit</code> command.</p>
<h2 id="toc-3-backend">3. Backend</h2>
<p>The first step is done. Now, we will create the backend. We will use Node.js and Express. We will also use Prisma to interact with the database.</p>
<p>Create a folder called <code>backend</code> at the root of the project.</p>
<pre><code class="hljsperl"><span class="hljs-keyword">mkdir</span> backend
</code></pre>
<p>Then open the folder in your terminal and initialize a Node.js project:</p>
<pre><code class="hljsperl">cd backend
npm init -<span class="hljs-keyword">y</span>
</code></pre>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ldb7sy3s10jrz2d72tqw.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>Install the dependencies:</p>
<ul>
<li>express: to create the server</li>
<li>prisma: to interact with the database</li>
<li>@prisma/client: to generate the code to interact with the database</li>
</ul>
<pre><code class="hljscss"><span class="hljs-selector-tag">npm</span> <span class="hljs-selector-tag">i</span> <span class="hljs-selector-tag">express</span> <span class="hljs-selector-tag">prisma</span> <span class="hljs-keyword">@prisma</span>/client
</code></pre>
<p>Initialize the prisma project:</p>
<pre><code class="hljs">npx prisma init
</code></pre>
<p>This initializes the prism project. We will use Prisma to interact with the database. Prisma will generate the code to interact with the database, so we don't have to write it ourselves.</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hd0en932j4nqui0ycnku.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>Open the file called <code>.env</code> and replace the content with the following:</p>
<pre><code class="hljsperl">DATABASE_URL=<span class="hljs-string">"postgresql://postgres:postgres@localhost:5432/postgres?schema=public"</span>
</code></pre>
<p>As a model for our project, we will use a 'User' with an id, a name, and an email. The id will be an autoincrementing integer, the name will be a string, and the email will also be a string.</p>
<p>Open the file <code>/prisma/schema.prisma</code> and replace the content with the following:</p>
<pre><code class="hljsjavascript"><span class="hljs-comment">// This is your Prisma schema file,</span>
<span class="hljs-comment">// learn more about it in the docs: https://pris.ly/d/prisma-schema</span>

generator client {
  provider = <span class="hljs-string">"prisma-client-js"</span>
}

datasource db {
  provider = <span class="hljs-string">"postgresql"</span>
  url      = env(<span class="hljs-string">"DATABASE_URL"</span>)
}

<span class="hljs-comment">//User with id as int autoincrement, name as string, email as string</span>
model User {
  id    Int    @id @<span class="hljs-keyword">default</span>(autoincrement())
  name  <span class="hljs-built_in">String</span>
  email <span class="hljs-built_in">String</span>
}
</code></pre>
<p>The file <code>schema.prisma</code> file should look like this:</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nydfxmxi9jgb5ytq4wyk.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>Now create a file called <code>index.js</code> inside the <code>/backend</code> folder and add the following content:</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> { PrismaClient } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'@prisma/client'</span>);

<span class="hljs-keyword">const</span> prisma = <span class="hljs-keyword">new</span> PrismaClient();
<span class="hljs-keyword">const</span> app = express();

<span class="hljs-comment">//use json</span>
app.use(express.json());

<span class="hljs-comment">//cors</span>
app.use(<span class="hljs-function">(<span class="hljs-params">req, res, next</span>) =&gt;</span> {
  res.setHeader(<span class="hljs-string">'Access-Control-Allow-Origin'</span>, <span class="hljs-string">'*'</span>);
  res.setHeader(<span class="hljs-string">'Access-Control-Allow-Methods'</span>, <span class="hljs-string">'GET, POST, PUT, DELETE'</span>);
  res.setHeader(<span class="hljs-string">'Access-Control-Allow-Headers'</span>, <span class="hljs-string">'Content-Type, Authorization'</span>);
  next();
});

<span class="hljs-comment">//test api with error handling</span>
app.get(<span class="hljs-string">'/test'</span>, (req, res, next) =&gt; {
  <span class="hljs-keyword">try</span> {
    res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Success!'</span> });
  } <span class="hljs-keyword">catch</span> (err) {
    next(err);
  }
});

<span class="hljs-comment">//get all users</span>
app.get(<span class="hljs-string">'/users'</span>, <span class="hljs-keyword">async</span> (req, res, next) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> users = <span class="hljs-keyword">await</span> prisma.user.findMany();
    res.status(<span class="hljs-number">200</span>).json(users);
  } <span class="hljs-keyword">catch</span> (err) {
    next(err);
  }
});

<span class="hljs-comment">//get user by id</span>
app.get(<span class="hljs-string">'/users/:id'</span>, <span class="hljs-keyword">async</span> (req, res, next) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> prisma.user.findUnique({
      <span class="hljs-attr">where</span>: { <span class="hljs-attr">id</span>: <span class="hljs-built_in">Number</span>(req.params.id) },
    });
    res.status(<span class="hljs-number">200</span>).json(user);
  } <span class="hljs-keyword">catch</span> (err) {
    next(err);
  }
});

<span class="hljs-comment">//create user</span>
app.post(<span class="hljs-string">'/users'</span>, <span class="hljs-keyword">async</span> (req, res, next) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> prisma.user.create({
      <span class="hljs-attr">data</span>: { ...req.body },
    });
    res.status(<span class="hljs-number">201</span>).json(user);
  } <span class="hljs-keyword">catch</span> (err) {
    next(err);
  }
});

<span class="hljs-comment">//update user</span>
app.put(<span class="hljs-string">'/users/:id'</span>, <span class="hljs-keyword">async</span> (req, res, next) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> prisma.user.update({
      <span class="hljs-attr">where</span>: { <span class="hljs-attr">id</span>: <span class="hljs-built_in">Number</span>(req.params.id) },
      <span class="hljs-attr">data</span>: { ...req.body },
    });
    res.status(<span class="hljs-number">200</span>).json(user);
  } <span class="hljs-keyword">catch</span> (err) {
    next(err);
  }
});

<span class="hljs-comment">//delete user</span>
app.delete(<span class="hljs-string">'/users/:id'</span>, <span class="hljs-keyword">async</span> (req, res, next) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> prisma.user.delete({
      <span class="hljs-attr">where</span>: { <span class="hljs-attr">id</span>: <span class="hljs-built_in">Number</span>(req.params.id) },
    });
    res.status(<span class="hljs-number">200</span>).json(user);
  } <span class="hljs-keyword">catch</span> (err) {
    next(err);
  }
});

<span class="hljs-comment">//Start server</span>
<span class="hljs-keyword">const</span> PORT = process.env.PORT || <span class="hljs-number">4000</span>;
app.listen(PORT, () =&gt; <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server running on port <span class="hljs-subst">${PORT}</span>`</span>));
</code></pre>
<p>For an explanation, check <a href="https://youtu.be/N-7uYm1PszM?si=khjth6OrHS-bSh7V&amp;t=484">here</a></p>
<p>Now you can generate the Prisma schema</p>
<pre><code class="hljs">npx prisma generate
</code></pre>
<p>Before we dockerize the backend, let's test it. Type in your terminal:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">node</span> <span class="hljs-selector-tag">index</span><span class="hljs-selector-class">.js</span>
</code></pre>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5n7gzz9z0b27bfxy9onj.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>Open your browser and go to <code>http://localhost:4000/test</code>. You should see the message <code>Success!</code>.</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f2csyv7half6wngzym5a.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>But if we go on <code>localhost:4000/users</code>, we actually see an error! This is because we don't have the schema in our database yet</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i87k1pkkx757wi8j5ldp.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>We can proceed with the dockerization part, and we will solve this problem later.</p>
<h3 id="dockerize-the-backend">Dockerize the backend</h3>
<p>Let's create 2 files, called <code>.dockerignore</code> and <code>backend.dockerfile</code> in the <code>backend</code> folder.</p>
<p>Open the file <code>.dockerignore</code> and add the following content:</p>
<pre><code class="hljs">**/node_modules
</code></pre>
<p>Open the file <code>backend.dockerfile</code> and add the following content:</p>
<pre><code class="hljsgo">FROM node:<span class="hljs-number">20</span>

WORKDIR /app

COPY <span class="hljs-keyword">package</span>*.json ./

RUN npm install

COPY prisma ./prisma

RUN npx prisma generate

COPY . .

EXPOSE <span class="hljs-number">4000</span>

CMD [<span class="hljs-string">"node"</span>, <span class="hljs-string">"index.js"</span>]
</code></pre>
<p>Let's update the <code>compose.yaml</code> file in the project's root, adding the <code>backend</code> service.</p>
<p>Below is the updated version:</p>
<pre><code class="hljsmarkdown">version: '3.9'

services:
  backend:
<span class="hljs-code">    container_name: backend</span>
<span class="hljs-code">    image: backend</span>
<span class="hljs-code">    build:</span>
<span class="hljs-code">      context: ./backend</span>
<span class="hljs-code">      dockerfile: backend.dockerfile</span>
<span class="hljs-code">    ports:</span>
<span class="hljs-bullet">      - </span>"4000:4000"
<span class="hljs-code">    environment:</span>
<span class="hljs-bullet">      - </span>DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres?schema=public
<span class="hljs-code">    depends_on:</span>
<span class="hljs-bullet">      - </span>db
  db:
<span class="hljs-code">    container_name: db</span>
<span class="hljs-code">    image: postgres:12</span>
<span class="hljs-code">    restart: always</span>
<span class="hljs-code">    environment:</span>
<span class="hljs-code">      POSTGRES_USER: postgres</span>
<span class="hljs-code">      POSTGRES_PASSWORD: postgres</span>
<span class="hljs-code">      POSTGRES_DB: postgres</span>
<span class="hljs-code">    ports:</span>
<span class="hljs-bullet">      - </span>"5432:5432"
<span class="hljs-code">    volumes:</span>
<span class="hljs-bullet">      - </span>pgdata:/var/lib/postgresql/data

volumes:
  pgdata: {}

</code></pre>
<p>Build the backend image:</p>
<pre><code class="hljs">docker compose build
</code></pre>
<p>Start the backend container:</p>
<pre><code class="hljs">docker compose up -d backend
</code></pre>
<p>Check if the container is running:</p>
<pre><code class="hljs">docker ps -a
</code></pre>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/739cdyvmwz4hppp9nyr1.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>Something interesting before we proceed or make any http requests.</p>
<pre><code class="hljspython">docker <span class="hljs-keyword">exec</span> -it db psql -U postgres
</code></pre>
<pre><code class="hljs">\dt
</code></pre>
<p>And we should see no relations.</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a8uvvkgix22qx9d1noir.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>Now type</p>
<pre><code class="hljspython">docker <span class="hljs-keyword">exec</span> -it backend npx prisma migrate dev --name init
</code></pre>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ghhcahvvoxxzxddio3ep.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<pre><code class="hljspython">docker <span class="hljs-keyword">exec</span> -it db psql -U postgres
</code></pre>
<pre><code class="hljs">\dt
</code></pre>
<p>And we should see the table <code>User</code> in the database. Of course it's still empy, but Prisma has created it for us. Cool!</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ox8d3zlnedv3ncedza1r.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<h3 id="add-users-in-3-different-ways">Add users in 3 different ways</h3>
<p>Now let's create 3 different users using 3 different ways.</p>
<ul>
<li>1 user using Prisma Studio</li>
<li>1 user using Postman</li>
<li>1 user using psql</li>
</ul>
<h4 id="create-a-user-using-prism-studio">Create a user using Prism Studio</h4>
<p>Open a new terminal and type:</p>
<pre><code class="hljs">npx prisma studio
</code></pre>
<p>This will open Prisma Studio in your browser at <code>http://localhost:5555</code> (Note: we are not using Docker to run Prisma Studio, we are running it directly on our machine).</p>
<p>Add a record: <code>userfromprisma</code> and <code>userfromprismamail</code></p>
<p>hit <code>Save 1 change</code>. You can leave Prisma Studio open in a tab, we will use it later.</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yzj2feb6vyvsz4m3eeg0.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>Now let's add another user using Postman (or any other tool you like).</p>
<p>If we make an http request to <code>http://localhost:4000/users</code> we should see 1 user (the one we just created using Prisma Studio).</p>
<pre><code class="hljsgo">{
  <span class="hljs-string">"name"</span>: <span class="hljs-string">"userfrompostman"</span>,
  <span class="hljs-string">"email"</span>: <span class="hljs-string">"userfrompostmanmail"</span>
}
</code></pre>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v8abwb9sivt82wx25oeo.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>If we check <code>localhost:4000/users</code> we should see 2 users now:</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5d19wjufg7gj9vcooscd.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>We can also check again on Prisma Studio, to check the consistency of our operations.</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bt1x7uiz7irqq5t534j1.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<h4 id="insert-user-from-psql">insert user from psql</h4>
<p>Let's insert another user using psql.</p>
<pre><code class="hljspython">docker <span class="hljs-keyword">exec</span> -it db psql -U postgres
\dt
</code></pre>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nv4oe1thd2sz9ylbg49i.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>Let's insert a new user manually using psql (don't do this in production, it's just for testing purposes!).</p>
<pre><code class="hljsperl">insert into <span class="hljs-string">"User"</span> (name, email) <span class="hljs-keyword">values</span> (<span class="hljs-string">'frompsql'</span>, <span class="hljs-string">'userfrompsqlmail'</span>);
<span class="hljs-keyword">select</span> * from <span class="hljs-string">"User"</span>;
</code></pre>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ar6kjscoxr31y6uzajmp.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>Let's check again on Prisma Studio, and we should see 3 users now:</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h6nrp68az0rmnexnbjn2.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<h2 id="toc-4-frontend">4. Frontend</h2>
<p>Now that we have the backend up and running, we can proceed with the frontend.</p>
<p>We will use Next.js 14 with TypeScript and Tailwind.</p>
<p>From the root folder of the project,</p>
<pre><code class="hljs">cd ..
</code></pre>
<p>And from the root folder of the project, run this command:</p>
<pre><code class="hljsperl">npx create-<span class="hljs-keyword">next</span>-app@latest --<span class="hljs-keyword">no</span>-git
</code></pre>
<p>We use the --no-git flag because we already initialized a git repository at the project's root.</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/13bumh193tu3w5sgfe1m.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>As options:</p>
<ul>
<li>What is your project named? <code>frontend</code></li>
<li>TypeScript? <code>Yes</code></li>
<li>EsLint? <code>Yes</code></li>
<li>Tailwind CSS? <code>Yes</code></li>
<li>Use the default directory structure? <code>Yes</code></li>
<li>App Router? <code>No</code> (not needed for this project)</li>
<li>Customize the default import alias? <code>No</code></li>
</ul>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jlkoc7r1k1c5wct7766a.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>This should create a new Next.js project in about one minute.</p>
<p>Step into the frontend folder:</p>
<pre><code class="hljs">cd frontend
</code></pre>
<p>Install Axios, we will use it to make HTTP requests (be sure to be in the <code>frontend</code> folder):</p>
<pre><code class="hljs">npm i axios
</code></pre>
<p>Before we proceed, try to run the project:</p>
<pre><code class="hljs">npm run dev
</code></pre>
<p>And open your browser at <code>http://localhost:3000</code>. You should see the default Next.js page.</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mxjlnxlmp365qfv95xcv.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<h4 id="modify-the-styles-global-css-file">Modify the styles/global.css file</h4>
<p>In the <code>src/frontend/src/styles/globals.css</code> file, repalce the content with this one (to avoid some problems with Tailwind):</p>
<pre><code class="hljscss"><span class="hljs-keyword">@tailwind</span> base;
<span class="hljs-keyword">@tailwind</span> components;
<span class="hljs-keyword">@tailwind</span> utilities;

<span class="hljs-selector-pseudo">:root</span> {
  <span class="hljs-attribute">--foreground-rgb</span>: <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>; 
  <span class="hljs-attribute">--background-start-rgb</span>: <span class="hljs-number">214</span>, <span class="hljs-number">219</span>, <span class="hljs-number">220</span>;
  <span class="hljs-attribute">--background-end-rgb</span>: <span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>;
}

<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">rgb</span>(var(--foreground-rgb));
  <span class="hljs-attribute">background</span>: <span class="hljs-built_in">linear-gradient</span>(
      to bottom,
      transparent,
      rgb(var(--background-end-rgb))
    )
    <span class="hljs-built_in">rgb</span>(var(--background-start-rgb));
}
</code></pre>
<h4 id="create-a-new-component">Create a new component</h4>
<p>In the <code>/frontend/src</code> folder, create a new folder called <code>components</code> and inside it create a new file called <code>CardComponent.tsx</code> and add the following content:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

interface Card {
  <span class="hljs-attr">id</span>: number; 
  name: string;
  email: string;
}

<span class="hljs-keyword">const</span> CardComponent: React.FC&lt;{ <span class="hljs-attr">card</span>: Card }&gt; = <span class="hljs-function">(<span class="hljs-params">{ card }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white shadow-lg rounded-lg p-2 mb-2 hover:bg-gray-100"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-sm text-gray-600"</span>&gt;</span>ID: {card.id}<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">className</span>=<span class="hljs-string">"text-lg font-semibold text-gray-800"</span>&gt;</span>{card.name}<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">className</span>=<span class="hljs-string">"text-md text-gray-700"</span>&gt;</span>{card.email}<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>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> CardComponent;

</code></pre>
<p>The file should look like this:</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2c9t9484e050p0nyxkmb.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>Now, open the <code>src/frontend/src/pages/index.tsx</code> file and replace the content with the following:</p>
<h4 id="populate-the-index-tsx-file">Populate the index.tsx file</h4>
<p>Open the file called <code>index.tsx</code> in the <code>frontend/src/pages</code> folder and replace the content with the following (<a href="https://youtu.be/N-7uYm1PszM?si=dy15D9w76EKpy76t&amp;t=1649">explanation here</a>) :</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>;
<span class="hljs-keyword">import</span> CardComponent <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/CardComponent'</span>;

interface User {
  <span class="hljs-attr">id</span>: number;
  name: string;
  email: string;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> apiUrl = process.env.NEXT_PUBLIC_API_URL || <span class="hljs-string">'http://localhost:4000'</span>;
  <span class="hljs-keyword">const</span> [users, setUsers] = useState&lt;User[]&gt;([]); 
  <span class="hljs-keyword">const</span> [newUser, setNewUser] = useState({ <span class="hljs-attr">name</span>: <span class="hljs-string">''</span>, <span class="hljs-attr">email</span>: <span class="hljs-string">''</span> }); 
  <span class="hljs-keyword">const</span> [updateUser, setUpdateUser] = useState({ <span class="hljs-attr">id</span>: <span class="hljs-string">''</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">''</span>, <span class="hljs-attr">email</span>: <span class="hljs-string">''</span> });

  <span class="hljs-comment">// Fetch users</span>
  useEffect(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> fetchData = <span class="hljs-keyword">async</span> () =&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">`<span class="hljs-subst">${apiUrl}</span>/users`</span>);
        setUsers(response.data.reverse());
      } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error fetching data:'</span>, error);
      }
    };

    fetchData();
  }, []);

  <span class="hljs-comment">// Create a user</span>
  <span class="hljs-keyword">const</span> createUser = <span class="hljs-keyword">async</span> (e: React.FormEvent&lt;HTMLFormElement&gt;) =&gt; {
    e.preventDefault();
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.post(<span class="hljs-string">`<span class="hljs-subst">${apiUrl}</span>/users`</span>, newUser);
      setUsers([response.data, ...users]);
      setNewUser({ <span class="hljs-attr">name</span>: <span class="hljs-string">''</span>, <span class="hljs-attr">email</span>: <span class="hljs-string">''</span> });
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error creating user:'</span>, error);
    }
  };

  <span class="hljs-comment">// Update a user</span>
  <span class="hljs-keyword">const</span> handleUpdateUser = <span class="hljs-keyword">async</span> (e: React.FormEvent&lt;HTMLFormElement&gt;) =&gt; {
    e.preventDefault();
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> axios.put(<span class="hljs-string">`<span class="hljs-subst">${apiUrl}</span>/users/<span class="hljs-subst">${updateUser.id}</span>`</span>, { <span class="hljs-attr">name</span>: updateUser.name, <span class="hljs-attr">email</span>: updateUser.email });
      setUpdateUser({ <span class="hljs-attr">id</span>: <span class="hljs-string">''</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">''</span>, <span class="hljs-attr">email</span>: <span class="hljs-string">''</span> });
      setUsers(
        users.map(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> {
          <span class="hljs-keyword">if</span> (user.id === <span class="hljs-built_in">parseInt</span>(updateUser.id)) {
            <span class="hljs-keyword">return</span> { ...user, <span class="hljs-attr">name</span>: updateUser.name, <span class="hljs-attr">email</span>: updateUser.email };
          }
          <span class="hljs-keyword">return</span> user;
        })
      );
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error updating user:'</span>, error);
    }
  };

  <span class="hljs-comment">// Delete a user</span>
  <span class="hljs-keyword">const</span> deleteUser = <span class="hljs-keyword">async</span> (userId: number) =&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> axios.delete(<span class="hljs-string">`<span class="hljs-subst">${apiUrl}</span>/users/<span class="hljs-subst">${userId}</span>`</span>);
      setUsers(users.filter(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> user.id !== userId));
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error deleting user:'</span>, error);
    }
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center justify-center min-h-screen p-4 bg-gray-100"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"space-y-4 w-full max-w-2xl"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-2xl font-bold text-gray-800 text-center"</span>&gt;</span>User Management App<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

        {/* Form to add new user */}
        <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{createUser}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-4 bg-blue-100 rounded shadow"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Name"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{newUser.name}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setNewUser({ ...newUser, name: e.target.value })}
            className="mb-2 w-full p-2 border border-gray-300 rounded"
          /&gt;

          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Email"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{newUser.email}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setNewUser({ ...newUser, email: e.target.value })}
            className="mb-2 w-full p-2 border border-gray-300 rounded"
          /&gt;
          <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">className</span>=<span class="hljs-string">"w-full p-2 text-white bg-blue-500 rounded hover:bg-blue-600"</span>&gt;</span>
            Add User
          <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>

        {/* Form to update user */}
        <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleUpdateUser}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-4 bg-green-100 rounded shadow"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"User ID"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{updateUser.id}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setUpdateUser({ ...updateUser, id: e.target.value })}
            className="mb-2 w-full p-2 border border-gray-300 rounded"
          /&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"New Name"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{updateUser.name}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setUpdateUser({ ...updateUser, name: e.target.value })}
            className="mb-2 w-full p-2 border border-gray-300 rounded"
          /&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"New Email"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{updateUser.email}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setUpdateUser({ ...updateUser, email: e.target.value })}
            className="mb-2 w-full p-2 border border-gray-300 rounded"
          /&gt;
          <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">className</span>=<span class="hljs-string">"w-full p-2 text-white bg-green-500 rounded hover:bg-green-600"</span>&gt;</span>
            Update User
          <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>

        {/* Display users */}
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"space-y-2"</span>&gt;</span>
          {users.map((user) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{user.id}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center justify-between bg-white p-4 rounded-lg shadow"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">CardComponent</span> <span class="hljs-attr">card</span>=<span class="hljs-string">{user}</span> /&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> deleteUser(user.id)} className="bg-red-500 hover:bg-red-600 text-white py-2 px-4 rounded"&gt;
                Delete User
              <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 class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
  );
}
</code></pre>
<p>We can create another user directly from the UI by adding a name and email.</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v15hgj5hsn8kwdi30evb.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>We can also update a user directly from the UI, adding the user id, the new name and the new email.</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bv9unlokolc1j2twlft1.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>If we check on <code>localhost:4000/users</code> we can see the changes we made using the UI</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/12wqok6t9ybezec2f1rh.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>You can also delete a user directly from the UI</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qma8q3lh3s9o4h26snh6.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<p>Now stop the Next.js app by hitting Ctrl-C or Cmd-c, so we leave port 3000 available for the Dockerized Next.js app</p>
<h4 id="dockerize-the-frontend">Dockerize the frontend</h4>
<p>Deploy a Next.js app with Docker.</p>
<p>Change the <code>next.config.js</code> file in the <code>frontend</code> folder, replacing it with the following content:</p>
<pre><code class="hljsjavascript"><span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('next').NextConfig}</span> </span>*/</span>
<span class="hljs-keyword">const</span> nextConfig = {
  <span class="hljs-attr">output</span>: <span class="hljs-string">'standalone'</span>
}

<span class="hljs-built_in">module</span>.exports = nextConfig
</code></pre>
<p>Create a file called <code>.dockerignore</code> in the <code>frontend</code> folder and add the following content:</p>
<pre><code class="hljs">**/node_modules
</code></pre>
<p>Create a file called <code>frontend.dockerfile</code> in the <code>frontend</code> folder and add the following content (it's directly from the vercel official docker example)</p>
<pre><code class="hljsphp">FROM node:<span class="hljs-number">18</span>-alpine <span class="hljs-keyword">AS</span> base

<span class="hljs-comment"># Install dependencies only when needed</span>
FROM base <span class="hljs-keyword">AS</span> deps
<span class="hljs-comment"># Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.</span>
RUN apk add --no-cache libc6-compat
WORKDIR /app

<span class="hljs-comment"># Install dependencies based on the preferred package manager</span>
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
  <span class="hljs-keyword">if</span> [ -f yarn.lock ]; then yarn --frozen-lockfile; \
  elif [ -f package-lock.json ]; then npm ci; \
  elif [ -f pnpm-lock.yaml ]; then yarn <span class="hljs-keyword">global</span> add pnpm &amp;&amp; pnpm i --frozen-lockfile; \
  <span class="hljs-keyword">else</span> <span class="hljs-keyword">echo</span> <span class="hljs-string">"Lockfile not found."</span> &amp;&amp; <span class="hljs-keyword">exit</span> <span class="hljs-number">1</span>; \
  fi


<span class="hljs-comment"># Rebuild the source code only when needed</span>
FROM base <span class="hljs-keyword">AS</span> builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

<span class="hljs-comment"># Next.js collects completely anonymous telemetry data about general usage.</span>
<span class="hljs-comment"># Learn more here: https://nextjs.org/telemetry</span>
<span class="hljs-comment"># Uncomment the following line in case you want to disable telemetry during the build.</span>
<span class="hljs-comment"># ENV NEXT_TELEMETRY_DISABLED 1</span>

RUN yarn build &amp;&amp; ls -l /app/.next


<span class="hljs-comment"># If using npm comment out above and use below instead</span>
<span class="hljs-comment"># RUN npm run build</span>

<span class="hljs-comment"># Production image, copy all the files and run next</span>
FROM base <span class="hljs-keyword">AS</span> runner
WORKDIR /app

ENV NODE_ENV production
<span class="hljs-comment"># Uncomment the following line in case you want to disable telemetry during runtime.</span>
<span class="hljs-comment"># ENV NEXT_TELEMETRY_DISABLED 1</span>

RUN addgroup --system --gid <span class="hljs-number">1001</span> nodejs
RUN adduser --system --uid <span class="hljs-number">1001</span> nextjs

COPY --from=builder /app/<span class="hljs-keyword">public</span> ./<span class="hljs-keyword">public</span>

<span class="hljs-comment"># Set the correct permission for prerender cache</span>
RUN mkdir .next
RUN chown nextjs:nodejs .next

<span class="hljs-comment"># Automatically leverage output traces to reduce image size</span>
<span class="hljs-comment"># https://nextjs.org/docs/advanced-features/output-file-tracing</span>
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/<span class="hljs-keyword">static</span> ./.next/<span class="hljs-keyword">static</span>

USER nextjs

EXPOSE <span class="hljs-number">3000</span>

ENV PORT <span class="hljs-number">3000</span>
<span class="hljs-comment"># set hostname to localhost</span>
ENV HOSTNAME <span class="hljs-string">"0.0.0.0"</span>

<span class="hljs-comment"># server.js is created by next build from the standalone output</span>
<span class="hljs-comment"># https://nextjs.org/docs/pages/api-reference/next-config-js/output</span>
CMD [<span class="hljs-string">"node"</span>, <span class="hljs-string">"server.js"</span>]
</code></pre>
<p>Now, let's update the <code>docker-compose.yml</code> file in the project's root, adding the <code>frontend</code> service.</p>
<p>Below the updated version:</p>
<pre><code class="hljsmarkdown">version: '3.9'

services:
  frontend:
<span class="hljs-code">    container_name: frontend</span>
<span class="hljs-code">    image: frontend</span>
<span class="hljs-code">    build:</span>
<span class="hljs-code">      context: ./frontend</span>
<span class="hljs-code">      dockerfile: frontend.dockerfile</span>
<span class="hljs-code">    ports:</span>
<span class="hljs-bullet">      - </span>'3000:3000'
<span class="hljs-code">    environment:</span>
<span class="hljs-bullet">      - </span>NEXT<span class="hljs-emphasis">_PUBLIC_</span>API_URL=http://localhost:4000
<span class="hljs-code">    restart: always</span>
<span class="hljs-code">    depends_on:</span>
<span class="hljs-bullet">      - </span>backend
  backend:
<span class="hljs-code">    container_name: backend</span>
<span class="hljs-code">    image: backend</span>
<span class="hljs-code">    build:</span>
<span class="hljs-code">      context: ./backend</span>
<span class="hljs-code">      dockerfile: backend.dockerfile</span>
<span class="hljs-code">    ports:</span>
<span class="hljs-bullet">      - </span>'4000:4000'
<span class="hljs-code">    environment:</span>
<span class="hljs-bullet">      - </span>DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres?schema=public
<span class="hljs-code">    depends_on:</span>
<span class="hljs-bullet">      - </span>db
  db:
<span class="hljs-code">    container_name: db</span>
<span class="hljs-code">    image: postgres:12</span>
<span class="hljs-code">    restart: always</span>
<span class="hljs-code">    environment:</span>
<span class="hljs-code">      POSTGRES_USER: postgres</span>
<span class="hljs-code">      POSTGRES_PASSWORD: postgresD</span>
<span class="hljs-code">      POSTGRES_DB: postgres</span>
<span class="hljs-code">    ports:</span>
<span class="hljs-bullet">      - </span>5432:5432
<span class="hljs-code">    volumes:</span>
<span class="hljs-bullet">      - </span>pgdata:/var/lib/postgresql/data

volumes:
  pgdata: {}
</code></pre>
<p>Build the frontend image:</p>
<pre><code class="hljs">docker compose build
</code></pre>
<p>Start the frontend container:</p>
<pre><code class="hljs">docker compose up -d frontend
</code></pre>
<h2 id="toc-5-final-test">5. Final test</h2>
<p>First, let's check if all the 3 containers are running:</p>
<pre><code class="hljs">docker ps -a
</code></pre>
<p>Now, open your browser at <code>http://localhost:3000</code>. You should see the app running, but we are running it with Docker this time.</p>
<p>You can, for example, create a new user directly from the UI</p>
<p><a href="https://youtu.be/N-7uYm1PszM"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/88i9dglk4by3dddcxr8z.png" alt="Build a FULL STACK Web app with Javascript API, Next.js 14, Node.js, Express, Prisma,Postgres,Docker"></a></p>
<h3 id="conclusion">Conclusion</h3>
<p>We have created a full stack app, using Docker to run the database, the backend, and the frontend. We have also used Docker Compose to run the database, the backend, and the frontend together.</p>
<p>We used many technologies, but we didn't install anything on our machine except Docker and Node.js.</p>
<ul>
<li>Next.js 14 (TypeScript)</li>
<li>Tailwind CSS</li>
<li>Node.js</li>
<li>Express (JavaScript)</li>
<li>Prisma</li>
<li>PostgreSQL</li>
<li>Docker</li>
<li>Docker Compose</li>
</ul>
<p>If you prefer a video version
<iframe width="100%" height="399" src="https://www.youtube.com/embed/N-7uYm1PszM" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
<!-- <iframe width="905" height="510" src="https://www.youtube.com/embed/N-7uYm1PszM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> -->
<p>All the code is available for free on <a href="https://youtu.be/N-7uYm1PszM">GitHub</a> (link in video description).</p>
<p>If you have any questions, comment below or in the <a href="https://youtu.be/N-7uYm1PszM">video comments</a></p>
<p>You can find me here:
<a href="https://francescociulla.com">Francesco</a></p>
]]></description>
                                                                    <author><![CDATA[Francesco Ciulla]]></author>
                <guid>https://devdojo.com/11832</guid>
                <pubDate>Tue, 12 Dec 2023 23:13:06 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[How to Install PHP 8.3 on Ubuntu 22.04]]></title>
                <link>https://devdojo.com/techvblogs/how-to-install-php-83-on-ubuntu-2204</link>
                <description><![CDATA[<p>PHP is a popular server scripting language known for creating dynamic and interactive web pages. Getting up and running with your language of choice is the first step in learning to program.</p>
<p><a href="https://www.php.net/">PHP</a> is a general-purpose scripting language suitable for web development. Most websites out there are programmed using PHP language because it is:</p>
<ul>
<li><strong>Fast</strong> – PHP is fast because it runs on its own memory space. Also, PHP has a Zend engine that parses PHP code and turns it into opcodes which are then interpreted.</li>
<li><strong>Flexible</strong> – PHP is flexible because it allows for almost all databases. MySQL is the de-facto database to connect to because it is open source.</li>
<li>Free and open-source</li>
<li><strong>PHP is forgiving</strong> – Why do we say PHP is forgiving? It is forgiving meaning its learning is not so hard and therefore suitable for almost all programmers starting out.</li>
<li>PHP supports major communication protocols i.e. LDAP, IMAP, and POP3.</li>
</ul>
<h2 id="what-s-new-features-in-php-8-3">What's New Features in PHP 8.3</h2>
<ul>
<li>Typed Class Constants</li>
<li>Added  <code>json_validate</code>  function</li>
<li>Dynamic class constant and Enum member fetch support</li>
<li><code>gc_status()</code>  returns additional GC information</li>
<li><code>Random</code>  extension: New  <code>\Random\Randomizer::getBytesFromString</code>  method</li>
<li><code>Random</code>  extension: New  <code>\Random\Randomizer::getFloat()</code>  and  <code>nextFloat()</code>  methods</li>
<li>Fallback value support for PHP INI Environment Variable syntax</li>
<li>PHP CLI Lint (<code>php -l</code>) supports linting multiple files at once</li>
<li><code>class_alias()</code>  supports aliasing built-in PHP classes</li>
<li>New  <code>stream_context_set_options</code>  function</li>
</ul>
<h2 id="syntax-functionality-changes-in-php-8-3">Syntax/Functionality Changes in PHP 8.3</h2>
<ul>
<li><code>unserialize()</code>: Upgrade  <code>E_NOTICE</code>  errors to  <code>E_WARNING</code></li>
<li><code>highlight_file</code>  and  <code>highlight_string</code>  output HTML changes</li>
<li>Granular  <code>DateTime</code>  Exceptions</li>
<li>Class constant type declarations in some PHP extension classes</li>
<li>Built-in CLI Server  <code>$_SERVER['SERVER_SOFTWARE']</code>  value changed for RFC3875 compliance</li>
</ul>
<p>Explore the official PHP 8.3 release notes for an in-depth look at the latest features, enhancements, and changes. Visit the PHP 8.3 release page on php.net to gain a comprehensive understanding of the updates, bug fixes, and optimizations introduced in this version. Stay informed and make the most out of PHP 8.3 for your web development projects. Check out the release notes here:  <a href="https://www.php.net/releases/8.3/en.php?ref=techvblogs.com">PHP 8.3 Release Notes</a>.</p>
<p>This tutorial will guide you through installing PHP 8.3 on Ubuntu and setting up a local programming environment via the command line.</p>
<h2 id="install-php-8-3-on-ubuntu-22-04">Install PHP 8.3 on Ubuntu 22.04</h2>
<h3 id="toc-1-run-system-updates">1. Run system updates</h3>
<p>The first thing to do in a new system is to update our repositories in order to make them up to date. Run upgrade command also.</p>
<pre><code class="hljsperl">sudo apt update &amp;&amp; apt upgrade -<span class="hljs-keyword">y</span>
</code></pre>
<h3 id="toc-2-add-ondrej-sury-ppa-repository">2. Add Ondrej Sury PPA repository</h3>
<p>To run PHP 8.3 on Ubuntu 22.04, we need to add Ondrej Sury PPA into our system. This is the maintainer of the PHP repository at the moment. This PPA is not currently checked so installing from it will not be guaranteed 100% results.</p>
<p>To add this PPA use the following command on our terminal.</p>
<pre><code class="hljspowershell">sudo <span class="hljs-built_in">add-apt</span><span class="hljs-literal">-repository</span> ppa:ondrej/php
</code></pre>
<p>After installation is complete we need to update the repositories again for the changes to take effect.</p>
<pre><code class="hljs">sudo apt update
</code></pre>
<p><strong>Read Also:</strong> <a href="https://techvblogs.com/blog/install-php-8-2-ubuntu-22-04">How to Install PHP 8.2 on Ubuntu 22.04</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-3-install-php-8-3-on-ubuntu-22-04">3. Install PHP 8.3 on Ubuntu 22.04</h3>
<p>We should now be able to install PHP 8.3 on Ubuntu 22.04 Linux machine. The commands to run are as shared below:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">sudo</span> <span class="hljs-selector-tag">apt</span> <span class="hljs-selector-tag">install</span> <span class="hljs-selector-tag">php8</span><span class="hljs-selector-class">.3</span> <span class="hljs-selector-tag">-y</span>
</code></pre>
<p>Check for the currently active version of PHP with the following command:</p>
<pre><code class="hljs">php --version
</code></pre>
<h3 id="toc-4-install-php-8-3-extensions">4. Install PHP 8.3 Extensions</h3>
<p>Besides PHP itself, you will likely want to install some additional PHP modules. You can use this command to install additional modules, replacing <code>PACKAGE_NAME</code> with the package you wish to install:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">sudo</span> <span class="hljs-selector-tag">apt-get</span> <span class="hljs-selector-tag">install</span> <span class="hljs-selector-tag">php8</span><span class="hljs-selector-class">.3-PACKAGE_NAME</span>
</code></pre>
<p>You can also install more than one package at a time. Here are a few suggestions of the most common modules you will most likely want to install:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">sudo</span> <span class="hljs-selector-tag">apt-get</span> <span class="hljs-selector-tag">install</span> <span class="hljs-selector-tag">-y</span> <span class="hljs-selector-tag">php8</span><span class="hljs-selector-class">.3-cli</span> <span class="hljs-selector-tag">php8</span><span class="hljs-selector-class">.3-common</span> <span class="hljs-selector-tag">php8</span><span class="hljs-selector-class">.3-fpm</span> <span class="hljs-selector-tag">php8</span><span class="hljs-selector-class">.3-mysql</span> <span class="hljs-selector-tag">php8</span><span class="hljs-selector-class">.3-zip</span> <span class="hljs-selector-tag">php8</span><span class="hljs-selector-class">.3-gd</span> <span class="hljs-selector-tag">php8</span><span class="hljs-selector-class">.3-mbstring</span> <span class="hljs-selector-tag">php8</span><span class="hljs-selector-class">.3-curl</span> <span class="hljs-selector-tag">php8</span><span class="hljs-selector-class">.3-xml</span> <span class="hljs-selector-tag">php8</span><span class="hljs-selector-class">.3-bcmath</span>
</code></pre>
<p>This command will install the following modules:</p>
<ul>
<li><code>php8.3-cli</code> - command interpreter, useful for testing PHP scripts from a shell or performing general shell scripting tasks</li>
<li><code>php8.3-common</code> - documentation, examples, and common modules for PHP</li>
<li><code>php8.3-mysql</code> - for working with MySQL databases</li>
<li><code>php8.3-zip</code> - for working with compressed files</li>
<li><code>php8.3-gd</code> - for working with images</li>
<li><code>php8.3-mbstring</code> - used to manage non-ASCII strings</li>
<li><code>php8.3-curl</code> - lets you make HTTP requests in PHP</li>
<li><code>php8.3-xml</code> - for working with XML data</li>
<li><code>php8.3-bcmath</code> - used when working with precision floats</li>
</ul>
<p>PHP configurations related to Apache are stored in <code>/etc/php/8.3/apache2/php.ini</code>. You can list all loaded PHP modules with the following command:</p>
<pre><code class="hljsperl">php -<span class="hljs-keyword">m</span>
</code></pre>
<p>You have installed PHP and verified the version you have running. You also installed any required PHP modules and were able to list the modules that you have loaded.</p>
<p>You could start using PHP right now, but you will likely want to use various libraries to build PHP applications quickly. Before you test your PHP environment, first set up a dependency manager for your projects.</p>
<p><strong>Thank you</strong> for reading this blog.</p>
<p><strong>Read Also:</strong> <a href="https://techvblogs.com/blog/update-composer-in-ubuntu">Update Composer In Ubuntu</a></p>
]]></description>
                                                            <category>php</category>
                                            <category>linux</category>
                                            <category>ubuntu</category>
                                            <category>php8.3</category>
                                            <category>php8</category>
                                                    <author><![CDATA[ ]]></author>
                <guid>https://devdojo.com/11810</guid>
                <pubDate>Thu, 23 Nov 2023 20:00:56 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[Build A Stunning Generative AI App with a React IDE]]></title>
                <link>https://devdojo.com/catalinpit/build-a-stunning-generative-ai-app-with-a-react-ide</link>
                <description><![CDATA[<p>If you've ever wondered how AI applications generate images from user prompts, you've come to the right place.</p>
<p>In this article, you will learn how these tools work by building an AI application to generate images.</p>
<h2 id="create-the-application">Create the application</h2>
<p>We'll start by creating a React + TypeScript application using Vite. Run the following command in your terminal:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">npm</span> <span class="hljs-selector-tag">create</span> <span class="hljs-selector-tag">vite</span><span class="hljs-keyword">@latest</span>
</code></pre>
<p>You'll then have to answer three questions so Vite understands how to configure your project.</p>
<pre><code class="hljs">✔ Project name: aimages
✔ Select a framework: › React
✔ Select a variant: › TypeScript
</code></pre>
<p>Lastly, you need to install and configure TailwindCSS.</p>
<p>Run the 2 commands below (in that order). The first command installs <code>tailwindcss</code> and its peer dependencies. The second command generates the <code>tailwind.config.js</code> and <code>postcss.config.js</code> files.</p>
<pre><code class="hljs">npm install -D tailwindcss postcss autoprefixer

npx tailwindcss init -p
</code></pre>
<p>Now open the <code>tailwind.config.js</code> file and modify the code as follows:</p>
<pre><code class="hljscss"><span class="hljs-comment">/** @type {import('tailwindcss').Config} */</span>
<span class="hljs-selector-tag">export</span> <span class="hljs-selector-tag">default</span> {
  <span class="hljs-attribute">content</span>: [
    <span class="hljs-string">"./index.html"</span>,
    <span class="hljs-string">"./src/**/*.{js,ts,jsx,tsx}"</span>,
  ],
  theme: {
    extend: {},
  },
  <span class="hljs-selector-tag">plugins</span>: <span class="hljs-selector-attr">[]</span>,
}
</code></pre>
<p>The above code configures TailwindCSS, specifying which files the framework should scan for class usage. It's set to check the <code>index.html</code> file and any JavaScript or TypeScript files in the <code>src</code> directory. It also lets you customize the theme, colors, fonts, and many other things.</p>
<p>Lastly, open <code>/src/styles.css</code> and add the TailwindCSS directives at the top of the 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="hljscss"><span class="hljs-keyword">@tailwind</span> base;
<span class="hljs-keyword">@tailwind</span> components;
<span class="hljs-keyword">@tailwind</span> utilities;
</code></pre>
<p>You're done with the project configuration! Now, upload the project on GitHub before proceeding to the next stage. You'll see later why!</p>
<h2 id="the-problem-with-building-user-interfaces">The problem with building user interfaces</h2>
<p>Before going further, let's highlight the main issues with building user interfaces.</p>
<p>One of the main issues with building user interfaces is that you don't have live visual feedback when building them. You make the changes in your editor, save them, and preview them in the browser. Then, if you're not happy with the changes, you have to repeat the process.</p>
<p>That results in a lot of switching between the IDE and the browser. While it might seem like a minor inconvenience, those seconds of waiting for the browser to refresh, plus the mental shift required, add up over time. Over the course of a day, this switching results in a substantial amount of wasted time.</p>
<p>It also results in a break of flow. Every disruption (like switching between applications) breaks the flow, leading to longer task completion times and possibly more errors.</p>
<p>On top of that, collaborating with others can be tedious and time-consuming. The usual way of collaborating involves working on the app, pushing the changes to GitHub, and deploying them. Only after that can the other collaborators check your work. That results in a substantial amount of time lost on unproductive things, such as waiting for the changes to deploy. So, the traditional approach can be quite cumbersome and inefficient.</p>
<p>In this article, you'll see a complementary tool you can use with your favorite IDE to build user interfaces better and faster.</p>
<h2 id="a-visual-react-ide">A visual React IDE</h2>
<p><a href="https://codux.hopp.to/catalin">Codux</a> is a free visual IDE for React + TypeScript applications. It enables you to work on the projects in real-time through a visual editor. All the changes you make to the component's properties, styles, and structure (JSX) are reflected automatically on the screen.</p>
<blockquote>
<p>Important mention: Codux is <strong>not meant to replace</strong> your favourite IDE. It's a complementary tool that you can use alongside your IDE.</p>
</blockquote>
<p>You can build and modify your interfaces using the panels and controllers provided by Codux. As the image shows, you can set CSS properties &amp; classes and different element states, for example.</p>
<p><img src="https://catalins.tech/content/images/2023/11/codux-interface.png" alt="Codux Interface"></p>
<p>And, of course, you also have the code editor at the bottom if you want to make specific changes by writing code.</p>
<blockquote>
<p>Note: Whether you make the changes using the visual editor, the built-in code editor, or your usual IDE, they will always be in sync!</p>
</blockquote>
<p>Codux also supports all the popular styling solutions such as CSS, Sass, CSS/Sass modules, Stylable, and TailwindCSS. The application we're building uses TailwindCSS, for example.</p>
<h3 id="component-playground">Component playground</h3>
<p>Another cool feature of Codux is the playground feature that enables you to share your work (components) with others.</p>
<p>In the playground mode, people can view and edit your components without setting up a local development environment and the other prerequisites.</p>
<p>Let's see it in action. At the beginning of the article, I mentioned that you must upload the project on GitHub. The reason is that the Playground requires your project to be hosted on GitHub.</p>
<p>To share your board (or component), click the "Share" button on the right.</p>
<p><em>Mention: The shared board is public, which means anyone with the link can see it. Be careful.</em></p>
<p><img src="https://catalins.tech/content/images/2023/11/share-project-codux.jpg" alt="Share Project Codux"></p>
<p>Codux will then ask you to create a link name for the board. Choose a name and click the button "Create Link" (the first time you do it) and "Publish" (afterwards).</p>
<p><em>Mention: Each time you make a change, you need to click the "Update Link" button before sharing the board. Otherwise, your changes won't be applied &amp; visible on the shared board.</em></p>
<p><img src="https://catalins.tech/content/images/2023/11/share-project-codux-customize-link.jpg" alt="Share Project Codux - Customize Sharing Link"></p>
<p>You can now copy the link and share it with other people. When they access the link, they'll see the online version of Codux (see the picture below).</p>
<p><img src="https://catalins.tech/content/images/2023/11/shared-project-codux.jpg" alt="Shared Project Codux"></p>
<p>Anyone with the link to the shared board can see it and make changes, including the text, colors, images, and even the code. That makes it an ideal tool for collaborating, where everyone can share &amp; apply their feedback in a simple and direct way.</p>
<p>This way, developers can focus on the work that matters rather than wasting valuable time pushing and pulling changes to/from each other.</p>
<p>It's important to mention, though, that the changes on the shared board don't affect the original board and won't be applied to it. That would be a cool feature, though!</p>
<h2 id="add-the-project-to-codux">Add the project to Codux</h2>
<p>Let's continue by importing the project we created earlier into Codux. Click on the "Open Local Project" option and select the project folder.</p>
<p><img src="https://catalins.tech/content/images/2023/11/import-local-project-codux.png" alt="Import a local project in Codux"></p>
<p>After that, Codux prompts you to run the configuration scripts, which install the required package to work on the project in this IDE.</p>
<p><img src="https://catalins.tech/content/images/2023/11/run-configuration-scripts-codux.png" alt="Run the configuration scripts for a newly imported project in Codux"></p>
<p>Once the installation is done, click "Scan for components" so Codux imports all the components.</p>
<p><img src="https://catalins.tech/content/images/2023/11/scan-for-components-codux.png" alt="Scan for the project components"></p>
<p>Then, you can see all the available components in the sidebar. In our case, we only have one - App - since it's a new project.</p>
<p>You can create new components by clicking the "+ New Component" button, but more on that later.</p>
<p><img src="https://catalins.tech/content/images/2023/11/components-page-codux.png" alt="Components page in Codux"></p>
<p>As you might've observed, there are "components" and "boards". A "Board" is a concept introduced by Codux, and it represents a fixture for your components so that Codux can automatically render it and allow you to edit it visually.</p>
<p>To create a "board", click on the component and then click the button "+ Create Board". That'll open a menu where you can set the starting point, the board name, and the board path.</p>
<p>You can leave the default options and click "Create".</p>
<p><img src="https://catalins.tech/content/images/2023/11/create-component-board-codux.png" alt="Create a board based on a component in Codux"></p>
<p>And this is the board view, where you can edit the components visually.</p>
<p><img src="https://catalins.tech/content/images/2023/11/board-view-codux.png" alt="The board view in Codux"></p>
<h2 id="configure-tailwindcss">Configure TailwindCSS</h2>
<p>For TailwindCSS to work in Codux, you need to perform some customizations in addition to the Tailwind setup process you did at the beginning of the article.</p>
<p>Create the <code>codux.config.js</code> in the root folder of the project, and add the following object:</p>
<pre><code class="hljspowershell">{
  <span class="hljs-string">"<span class="hljs-variable">$schema</span>"</span>: <span class="hljs-string">"https://wixplosives.github.io/codux-config-schema/codux.config.schema.json"</span>,
  <span class="hljs-string">"boardGlobalSetup"</span>: <span class="hljs-string">"./src/_codux/board-global-setup.ts"</span>
}
</code></pre>
<p>Then, create the <code>board-global-setup.ts</code> in the <code>./src/_codux</code> folder and add the following import:</p>
<pre><code class="hljsgo"><span class="hljs-keyword">import</span> <span class="hljs-string">"../index.css"</span>;
</code></pre>
<p>Done! Now, you can see the TailwindCSS classes in the Codux IDE.</p>
<blockquote>
<p>For more information about this configuration, check this <a href="https://help.codux.com/kb/en/article/using-tailwind-css-with-codux#1.-configure-codux.config.json">link</a>.</p>
</blockquote>
<h2 id="build-the-app">Build the app</h2>
<p>In the following sections, we'll start building the application.</p>
<h3 id="create-the-components-folder">Create the <code>components</code> folder</h3>
<p>Let's start by creating a <code>component</code> folder for all the components. You can either do it from your IDE, terminal, or Codux. This is how you can do it from Codux:</p>
<p><img src="https://catalins.tech/content/images/2023/11/create-new-folder-codux.png" alt="Create components in Codux"></p>
<p>Click the highlighted icon to see the project files. After that, create the folder as you would create it on your machine. Right-click on the <code>src</code> folder and click <code>New Folder</code>.</p>
<h5 id="creating-components-from-codux">Creating components from Codux</h5>
<p>If you want to create components using the interactive process from Codux, you need to set the components path. You can do that by adding this code in <code>codux.config.json</code>:</p>
<pre><code class="hljspowershell">{
  <span class="hljs-string">"<span class="hljs-variable">$schema</span>"</span>: <span class="hljs-string">"https://wixplosives.github.io/codux-config-schema/codux.config.schema.json"</span>,
  <span class="hljs-string">"boardGlobalSetup"</span>: <span class="hljs-string">"./src/_codux/board-global-setup.ts"</span>,
  <span class="hljs-string">"newComponent"</span>: {
    <span class="hljs-string">"componentsPath"</span>: <span class="hljs-string">"src/components"</span>
  }
}
</code></pre>
<p>Now, you can create components using the visual interface.</p>
<h3 id="layout-component">Layout component</h3>
<p>Create the <code>Layout</code> component in the <code>components</code> folder. We'll use the layout on all the pages.</p>
<p>You can follow the next steps using your usual IDE or Codux.</p>
<p><em>Tip 💡: You're fine either way because the code is automatically in sync between the two. Whether you use Codux or your editor, both will contain the same code without you having to do anything. That's neat!</em></p>
<p>Now open the <code>Layout</code> component and add the following code:</p>
<pre><code class="hljsjavascript">type LayoutProps = {
  <span class="hljs-attr">children</span>: React.ReactNode;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Layout</span>(<span class="hljs-params">{ children }: LayoutProps</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
}
</code></pre>
<h3 id="refactor-app-tsx">Refactor <code>App.tsx</code></h3>
<p>Then go to the <code>App.tsx</code> file and replace the content with the following code:</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">import</span> Layout <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Layout"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [inputValue, setInputValue] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [imageURL, setImageURL] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [loading, setLoading] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [error, setError] = useState&lt;<span class="hljs-literal">null</span> | string&gt;(<span class="hljs-literal">null</span>);

  <span class="hljs-keyword">const</span> fetchImage = <span class="hljs-keyword">async</span> (prompt: string) =&gt; {
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"http://localhost:3000/api/genimg"</span>, {
      <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">value</span>: prompt }),
    });

    <span class="hljs-keyword">if</span> (response.ok) {
      <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.json();

      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Array</span>.isArray(data) &amp;&amp; data.length &gt; <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">return</span> data[<span class="hljs-number">0</span>];
      }

      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Unexpected server response"</span>);
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(response.statusText);
    }
  };

  <span class="hljs-keyword">const</span> onInputChange = <span class="hljs-function">(<span class="hljs-params">e: React.ChangeEvent&lt;HTMLTextAreaElement&gt;</span>) =&gt;</span> {
    setInputValue(e.target.value);
  };

  <span class="hljs-keyword">const</span> handleSubmit = <span class="hljs-keyword">async</span> (e: React.FormEvent&lt;HTMLFormElement&gt;) =&gt; {
    e.preventDefault();
    setLoading(<span class="hljs-literal">true</span>);
    setError(<span class="hljs-literal">null</span>);

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> url = <span class="hljs-keyword">await</span> fetchImage(inputValue);
      setImageURL(url);
    } <span class="hljs-keyword">catch</span> (err) {
      <span class="hljs-keyword">if</span> (err <span class="hljs-keyword">instanceof</span> <span class="hljs-built_in">Error</span>) {
        setError(err.message);
      } <span class="hljs-keyword">else</span> {
        setError(<span class="hljs-string">"An unexpected error occurred"</span>);
      }
    } <span class="hljs-keyword">finally</span> {
      setLoading(<span class="hljs-literal">false</span>);
    }
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Layout</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">section</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">h1</span>&gt;</span>PromptPix AI<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
                Dive into the world of AI-driven creativity with PromptPix AI,
                where your words become vivid visuals. Simply input your idea,
                and watch as our advanced algorithms craft the image you
                envisioned.
              <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">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleSubmit}</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">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"prompt"</span>&gt;</span>Image prompt<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
                  <span class="hljs-attr">id</span>=<span class="hljs-string">"prompt"</span>
                  <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter your prompt"</span>
                  <span class="hljs-attr">value</span>=<span class="hljs-string">{inputValue}</span>
                  <span class="hljs-attr">onChange</span>=<span class="hljs-string">{onInputChange}</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">disabled</span>=<span class="hljs-string">{inputValue.length</span> === <span class="hljs-string">0}</span>&gt;</span>
                Generate
              <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>
            {loading &amp;&amp; (
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Your image is being generated...<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>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</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">width</span>=<span class="hljs-string">"44"</span>
                        <span class="hljs-attr">height</span>=<span class="hljs-string">"44"</span>
                        <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span>
                        <span class="hljs-attr">strokeWidth</span>=<span class="hljs-string">"2"</span>
                        <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span>
                        <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span>
                        <span class="hljs-attr">strokeLinecap</span>=<span class="hljs-string">"round"</span>
                        <span class="hljs-attr">strokeLinejoin</span>=<span class="hljs-string">"round"</span>
                      &gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
                          <span class="hljs-attr">stroke</span>=<span class="hljs-string">"none"</span>
                          <span class="hljs-attr">d</span>=<span class="hljs-string">"M0 0h24v24H0z"</span>
                          <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</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">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M12 6l0 -3"</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">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M16.25 7.75l2.15 -2.15"</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">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M18 12l3 0"</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">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M16.25 16.25l2.15 2.15"</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">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M12 18l0 3"</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">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M7.75 16.25l-2.15 2.15"</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">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M6 12l-3 0"</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">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M7.75 7.75l-2.15 -2.15"</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">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">div</span>&gt;</span>
            )}

            {imageURL &amp;&amp; !loading &amp;&amp; (
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Find your generated image below<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
                  <span class="hljs-attr">src</span>=<span class="hljs-string">{imageURL}</span>
                  <span class="hljs-attr">width</span>=<span class="hljs-string">{600}</span>
                  <span class="hljs-attr">height</span>=<span class="hljs-string">{600}</span>
                  <span class="hljs-attr">alt</span>=<span class="hljs-string">"Generated image"</span>
                /&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            )}

            {!imageURL &amp;&amp; !loading &amp;&amp; (
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Your image will appear below<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>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</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">width</span>=<span class="hljs-string">"44"</span>
                        <span class="hljs-attr">height</span>=<span class="hljs-string">"44"</span>
                        <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span>
                        <span class="hljs-attr">strokeWidth</span>=<span class="hljs-string">"2"</span>
                        <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span>
                        <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span>
                        <span class="hljs-attr">strokeLinecap</span>=<span class="hljs-string">"round"</span>
                        <span class="hljs-attr">strokeLinejoin</span>=<span class="hljs-string">"round"</span>
                      &gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
                          <span class="hljs-attr">stroke</span>=<span class="hljs-string">"none"</span>
                          <span class="hljs-attr">d</span>=<span class="hljs-string">"M0 0h24v24H0z"</span>
                          <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</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">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M15 8h.01"</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">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M11.5 21h-5.5a3 3 0 0 1 -3 -3v-12a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v5.5"</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">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M18 18m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0"</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">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M20.2 20.2l1.8 1.8"</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">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M3 16l5 -5c.928 -.893 2.072 -.893 3 0l2 2"</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">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">div</span>&gt;</span>
            )}

            {error &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{error}<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">section</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>This component contains the barebone application without any style applied.</p>
<p><img src="https://catalins.tech/content/images/2023/11/barebone-app-codux.png" alt="The app preview in Codux"></p>
<p><em>Note: Everything is in the <code>App</code> file for illustrative purposes. It's easier to showcase the tool this way. You'll see the final application on GitHub.</em></p>
<h3 id="style-the-app-with-codux">Style the app with Codux</h3>
<p>Let's start by styling the <code>Layout</code> component first. Double click <code>App</code> in the <code>Elements</code> tab (left-hand side column).</p>
<p>That opens a new tree, where you should see the <code>Layout</code> component. Double click on <code>Layout</code>, and then select <code>main</code>.</p>
<p>The <code>Elements</code> tab shows you all the HTML elements from your code.</p>
<p><img src="https://catalins.tech/content/images/2023/11/main-div.png" alt="Main div"></p>
<p>To add Tailwind classes to HTML elements, click the "Properties" icon from the right-hand side. Then, search for the "className" property.</p>
<p>Once there, add the following Tailwind classes:</p>
<pre><code class="hljs">flex min-h-screen flex-col justify-center items-center
</code></pre>
<p>These classes set up a flex container with a minimum height equal to the viewport height, arrange its children in a column, and center them vertically and horizontally within the container.</p>
<p>You should see the changes reflected immediately, both visually and in the code.</p>
<p><img src="https://catalins.tech/content/images/2023/11/classes-applied.png" alt="Applying Tailwind classes in Codux"></p>
<p>Pretty cool, right?</p>
<p>Now, let's switch to your favorite IDE to see how easy it is to use them together. Open the <code>index.css</code> file and add this block:</p>
<pre><code class="hljsruby">body {
  @apply bg-gray-<span class="hljs-number">50</span>;
}
</code></pre>
<h3 id="app-component">App component</h3>
<p>Continue by adding the following classes to the <code>section</code> element from the <code>App</code> component:</p>
<pre><code class="hljscss"><span class="hljs-selector-tag">container</span> <span class="hljs-selector-tag">flex</span> <span class="hljs-selector-tag">flex-col</span> <span class="hljs-selector-tag">gap-6</span> <span class="hljs-selector-tag">py-8</span> <span class="hljs-selector-tag">md</span><span class="hljs-selector-pseudo">:max-w-</span><span class="hljs-selector-attr">[64rem]</span> <span class="hljs-selector-tag">md</span><span class="hljs-selector-pseudo">:py-12</span> <span class="hljs-selector-tag">lg</span><span class="hljs-selector-pseudo">:py-24</span>
</code></pre>
<p>These classes modify the section to be a flexible container with column direction, vertical gap between child elements of 6 units, vertical padding of 8 units (increased to 12 units on medium screens and 24 units on large screens), and a maximum width of 64rem on medium and larger screens.</p>
<p><img src="https://catalins.tech/content/images/2023/11/section-classes.png" alt="Applying Tailwind classes in Codux"></p>
<p>Similarly, apply the following classes to the first <code>div</code> element:</p>
<pre><code class="hljs">bg-white shadow-md rounded border border-slate-200 p-14 text-center
</code></pre>
<p>These TailwindCSS utility classes apply a white background, a medium shadow, rounded corners, a slate-colored border, padding on all sides of 14 units, and center-aligned text.</p>
<p><img src="https://catalins.tech/content/images/2023/11/div-classes.png" alt="Applying Tailwind classes in Codux"></p>
<p>This is how the application looks up to this point.</p>
<p><img src="https://catalins.tech/content/images/2023/11/app-progress.png" alt="A screenshot of the Vite + React + TypeScript app built with Codux"></p>
<p>The next steps are as follows:</p>
<ul>
<li>stylize the heading + description</li>
<li>stylize the form</li>
<li>stylize the part where the image will appear</li>
<li>create the API</li>
</ul>
<p>Let's style the heading and description through your IDE (VS Code, in my case) to see the auto-sync in action.</p>
<p>Replace the 2nd <code>div</code> element with the following code:</p>
<pre><code class="hljsxml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mx-auto flex w-full flex-col md:max-w-[58rem]"</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-heading text-2xl mb-4 sm:text-4xl text-center"</span>&gt;</span>
      PromptPix AI
   <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">className</span>=<span class="hljs-string">"text-sm sm:text-base text-center mb-4"</span>&gt;</span>
      Dive into the world of AI-driven creativity with PromptPix AI,
      where your words become vivid visuals. Simply input your idea,
      and watch as our advanced algorithms craft the image you
      envisioned.
   <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>
</code></pre>
<p>The screenshot below illustrates how your code should look:</p>
<p><img src="https://catalins.tech/content/images/2023/11/code-editor-screenshot.png" alt="Cursor.sh screenshot of the code built with Codux"></p>
<p>If you navigate back to Codux, you will see the changes applied in the visual area and the code editor from the bottom of the page.</p>
<p>Similarly, all the changes made in Codux are available in your IDE.</p>
<p><img src="https://catalins.tech/content/images/2023/11/codux-screenshot.png" alt="Codux screenshot"></p>
<p>As an exercise, add the following classes to these elements:</p>
<ul>
<li>the <code>form</code> element - <code>space-y-6</code></li>
<li>the <code>div</code> element inside the form - <code>flex flex-col items-center gap-4</code></li>
<li>the <code>label</code> element - <code>text-sm sm:text-base</code></li>
<li>the <code>textarea</code> element - <code>border border-slate-200 rounded p-2 w-3/4 h-32 resize-none</code></li>
<li>the <code>button</code> element:</li>
</ul>
<pre><code class="hljsjavascript"><span class="hljs-string">`<span class="hljs-subst">${
inputValue.length === <span class="hljs-number">0</span>
? <span class="hljs-string">"bg-white text-gray-800 font-semibold py-2 px-4 border border-slate-400 rounded shadow opacity-50 hover:opacity-50 cursor-not-allowed"</span>
: <span class="hljs-string">"bg-white hover:bg-slate-100 text-gray-800 font-semibold py-2 px-4 border border-slate-400 rounded shadow"</span>
}</span>`</span>
</code></pre>
<p>This is how the application looks like with all the above classes applied:</p>
<p><img src="https://catalins.tech/content/images/2023/11/codux-app-screenshot-progress.png" alt="Screenshot of Codux running React, TypeScript and TailwindCSS"></p>
<p>Looks pretty good already, right?</p>
<p>The last step involves styling the part where the generated image will appear. We'll make the initial <code>div</code> element (that's displayed before entering any prompt) a flex container with its children arranged in a column, centered horizontally, with a gap of 24px between each child and a top margin of 48px - <code>flex flex-col items-center gap-6 mt-12</code>.</p>
<p>You can add them as usual using the "className" field from Codux.</p>
<p><img src="https://catalins.tech/content/images/2023/11/codux-classes-img.png" alt="Codux TailwindCSS classes"></p>
<p>Apply the same Tailwind classes to the other 2 <code>div</code> elements that display the loading state and the generated image.</p>
<p>Lastly, stylize the <code>error</code>:</p>
<pre><code class="hljsxml">{error &amp;&amp; (
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-center w-1/2 mx-auto bg-red-500 mt-2 rounded border border-red-800"</span>&gt;</span>
   {error}
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
)}
</code></pre>
<p>Now you're done with the user interface!</p>
<h3 id="codux-offers-much-more">Codux offers much more</h3>
<p>In building this application, you mostly applied classes and visualized the results in real-time. However, Codux offers much more.</p>
<p>With Codux, you can test your application on multiple viewports and make the changes live for each viewport.</p>
<p><img src="https://catalins.tech/content/images/2023/11/codux-responsive.png" alt="Codux responsiveness feature"></p>
<p>Besides that, we added the classes manually for each element. But you can also use the "Computed Styles" tab to stylize the elements. Instead of writing code manually, you can style elements interactively.</p>
<p><img src="https://catalins.tech/content/images/2023/11/codux-computed-styles.png" alt="Codux computed styles"></p>
<p>A favorite feature of mine is the ability to open the code for a specific element. You can select the particular element, right-click on it, and open it in the code editor/IDE.</p>
<p>The image illustrates how Codux highlights the relevant code for the selected element. Neat, right?</p>
<p><img src="https://catalins.tech/content/images/2023/11/open-code.png" alt="Open code from Codux"></p>
<p>As you can see, there are lots of cool features in Codux. I encourage you to <a href="https://codux.hopp.to/catalin">download it</a> and explore it. It's free!</p>
<p>Some valuable resources to help you:</p>
<ul>
<li><a href="https://youtu.be/e7lcUQyIxKM?si=vvT66TjqsclE8jYK">Build React Apps with Codux Visual IDE for Faster UI Workflow</a></li>
<li><a href="https://youtu.be/fly6qAcBxi8">Learn How to Build a Standout Portfolio Website Using Codux</a></li>
<li><a href="https://youtu.be/_6k9Bqr0I0A">Craft a Real-Time Weather App From the Ground Up With Codux</a></li>
<li><a href="https://youtu.be/35oplanvybI">Learn All About Visual Component Styling, Including Class Creation and Shaping State, Variables and Computed Styles</a></li>
</ul>
<h2 id="the-backend-code-for-generating-the-image">The Backend Code for Generating the Image</h2>
<p>You are probably using a different programming language &amp; framework than me. Instead of wasting your time taking you through a painful process of building the backend in a technology you're not interested in, I'll share the code for the API route straightaway.</p>
<pre><code class="hljsjavascript"><span class="hljs-keyword">import</span> express, { Request, Response } <span class="hljs-keyword">from</span> <span class="hljs-string">"express"</span>;
<span class="hljs-keyword">import</span> Replicate <span class="hljs-keyword">from</span> <span class="hljs-string">"replicate"</span>;
<span class="hljs-keyword">import</span> cors <span class="hljs-keyword">from</span> <span class="hljs-string">"cors"</span>;

<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> port = <span class="hljs-number">3030</span>;

app.use(express.json());
app.use(cors());

app.options(<span class="hljs-string">"/api/genimg"</span>, (req: Request, <span class="hljs-attr">res</span>: Response) =&gt; {
  <span class="hljs-comment">// Pre-flight request. Reply successfully:</span>
  res.status(<span class="hljs-number">200</span>).end();
});

app.post(<span class="hljs-string">"/api/genimg"</span>, <span class="hljs-keyword">async</span> (req: Request, <span class="hljs-attr">res</span>: Response) =&gt; {
  <span class="hljs-keyword">const</span> { value } = req.body;

  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> replicate = <span class="hljs-keyword">new</span> Replicate({
      <span class="hljs-attr">auth</span>: process.env.REPLICATE_API_TOKEN || <span class="hljs-string">""</span>,
    });

    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> replicate.run(
      <span class="hljs-string">"stability-ai/stable-diffusion:a00d0b7dcbb9c3fbb34ba87d2d5b46c56969c84a628bf778a7fdaec30b1b99c5"</span>,
      {
        <span class="hljs-attr">input</span>: {
          <span class="hljs-attr">prompt</span>: value,
          <span class="hljs-attr">image_dimensions</span>: <span class="hljs-string">"1024x1024"</span>,
          <span class="hljs-attr">num_inference_steps</span>: <span class="hljs-number">50</span>,
          <span class="hljs-attr">num_outputs</span>: <span class="hljs-number">1</span>,
          <span class="hljs-attr">guideance_scale</span>: <span class="hljs-number">7.5</span>,
          <span class="hljs-attr">prompt_strength</span>: <span class="hljs-number">0.8</span>,
          <span class="hljs-attr">scheduler</span>: <span class="hljs-string">"KarrasDPM"</span>,
        },
      }
    );

    res.status(<span class="hljs-number">200</span>).json(result);
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Failed to generate image. Error:"</span>, error);
    res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Failed to generate image"</span> });
  }
});

app.all(<span class="hljs-string">"/api/genimg"</span>, (req: Request, <span class="hljs-attr">res</span>: Response) =&gt; {
  res.status(<span class="hljs-number">405</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Method Not Allowed"</span> });
});

app.listen(port, () =&gt; {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server running at http://localhost:<span class="hljs-subst">${port}</span>`</span>);
});
</code></pre>
<p>This TypeScript code sets up an Express server that listens on port 3030 and has CORS enabled. It defines three routes, all for the path "/api/genimg". The OPTIONS route is for handling pre-flight CORS requests. The POST route accepts a JSON body with a "value" property, uses it as a prompt to generate an image using the Replicate API, and returns the result. If there's an error during this process, it logs it and returns a 500 status code with a failure message. The ALL route is a catch-all that returns a 405 status code, indicating that the method used is not allowed. The server listens on the specified port and logs a message to the console when it's running.</p>
<p>This way, you can adapt the code to your programming language &amp; framework of your choice.</p>
<p><em>Note: You can check the whole application code (both server and client) by accessing this <a href="https://github.com/catalinpit/aimages">GitHub link</a>.</em></p>
<h2 id="an-ending-note">An ending note</h2>
<p>Building applications comes with challenges that can slow down the development process and make collaboration difficult. One of the main challenges is the constant need to switch between different tools. That takes up considerable time and can break your concentration and flow. Moreover, collaborating with others is cumbersome, often requiring several steps before others can test your work.</p>
<p>This is where Codux steps in to make the whole process smoother and more efficient. Codux enables you to visualize changes as you make them without constantly switching between multiple tools. Moreover, it offers a visual user interface that makes it easier to build applications by allowing you to create things with a couple of clicks. But perhaps most importantly, it allows for real-time collaboration, enabling people to work together seamlessly.</p>
<p>Before closing, I want to mention again that Codux is not here to replace your IDE and other tools you might use. Think of Codux as another tool in your developer toolbox.</p>
<p><em>The article was originally published on my blog - <a href="https://catalins.tech">catalins.tech</a></em></p>
]]></description>
                                                                    <author><![CDATA[ ]]></author>
                <guid>https://devdojo.com/11799</guid>
                <pubDate>Mon, 20 Nov 2023 01:06:22 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[Conditionally apply Laravel Soft Deleting scope]]></title>
                <link>https://devdojo.com/tinahammar/conditionally-apply-laravel-soft-deleting-scope</link>
                <description><![CDATA[<p>Create an App state service class</p>
<pre><code class="hljsxml"><span class="php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Services</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppState</span>
</span>{
    <span class="hljs-keyword">public</span> bool $softDeletingScope = <span class="hljs-keyword">true</span>;
}
</span></code></pre>
<p>Register the service in any service provider</p>
<pre><code class="hljsphp"><span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Services</span>\<span class="hljs-title">AppState</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">App</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-keyword">$this</span>-&gt;registerAppStateService();
}

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">boot</span><span class="hljs-params">()</span>: <span class="hljs-title">void</span>
</span>{
    <span class="hljs-keyword">$this</span>-&gt;controlSoftDeletingScope();
}

<span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">registerAppStateService</span><span class="hljs-params">()</span>: <span class="hljs-title">void</span>
</span>{
    <span class="hljs-keyword">$this</span>-&gt;app-&gt;singleton(AppState::class, fn (): AppState =&gt; <span class="hljs-keyword">new</span> AppState());
}

<span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">controlSoftDeletingScope</span><span class="hljs-params">()</span>: <span class="hljs-title">void</span>
</span>{
    App::macro(<span class="hljs-string">'enableSoftDeletingScope'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span>: <span class="hljs-title">void</span> </span>{
        app(AppState::class)-&gt;softDeletingScope = <span class="hljs-keyword">true</span>;
    });

    App::macro(<span class="hljs-string">'disableSoftDeletingScope'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span>: <span class="hljs-title">void</span> </span>{
        app(AppState::class)-&gt;softDeletingScope = <span class="hljs-keyword">false</span>;
    });

    App::macro(<span class="hljs-string">'allowsSoftDeletingScope'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span>: <span class="hljs-title">bool</span> </span>{
        <span class="hljs-keyword">return</span> app(AppState::class)-&gt;softDeletingScope;
    });
}
</code></pre>
<p>Now you have access to these helpers anywhere in your code.</p>
<pre><code class="hljsphp"><span class="hljs-comment">//Enable/disable the soft deleting scope</span>

App::enableSoftDeletingScope()

App::disableSoftDeletingScope()

<span class="hljs-comment">//Check if soft deleting scope is allowed or not.</span>

App::allowsSoftDeletingScope() <span class="hljs-comment">//returns true/false</span>

<span class="hljs-comment">//All helpers are available as not static versions as well</span>
app()-&gt;enableSoftDeletingScope()
app()-&gt;disableSoftDeletingScope()
app()-&gt;allowsSoftDeletingScope()
</code></pre>
<p>Create a trait that conditionally applies the SoftDeletingScope.
Use this trait instead of <code>SoftDeletes</code> on your Model.</p>
<pre><code class="hljsxml"><span class="php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">ModelTraits</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">SoftDeletes</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">SoftDeletingScope</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">App</span>;

<span class="hljs-keyword">trait</span> conditionalSoftDeletes
{
    <span class="hljs-keyword">use</span> <span class="hljs-title">SoftDeletes</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">bootSoftDeletes</span><span class="hljs-params">()</span>: <span class="hljs-title">void</span>
    </span>{
        <span class="hljs-keyword">if</span> (App::allowsSoftDeletingScope()) {
            <span class="hljs-keyword">static</span>::addGlobalScope(<span class="hljs-keyword">new</span> SoftDeletingScope);
        }
    }
}
</span></code></pre>
<h3 id="example-use-case-in-a-job">Example use case in a job</h3>
<pre><code class="hljsphp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FooJob</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ShouldQueue</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span><span class="hljs-params">(
        protected FooModel $model
    )</span> </span>{ }


    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handle</span><span class="hljs-params">()</span>: <span class="hljs-title">void</span>
    </span>{
        app()-&gt;disableSoftDeletingScope();

        <span class="hljs-comment">//Now you could load missing relations, that includes soft deleted records</span>
        <span class="hljs-keyword">$this</span>-&gt;model-&gt;loadMissing( ... )

        app()-&gt;enableSoftDeletingScope(); <span class="hljs-comment">// Don't forget to enable it again</span>
    }
</code></pre>
<h3 id="important">Important</h3>
<p>Please observe that methods registered in the SoftDeletingScope class (withTrashed(), withoutTrashed(), onlyTrashed() and more) will not be available while the scope is disabled.</p>
<p>This article is related to this post:
<a href="https://devdojo.com/tinahammar/helper-to-detect-if-laravel-is-running-a-job">Laravel helper to detect if Laravel is running a job</a></p>
]]></description>
                                                                    <author><![CDATA[ ]]></author>
                <guid>https://devdojo.com/11791</guid>
                <pubDate>Wed, 15 Nov 2023 09:58:29 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[Helper to detect if Laravel is running a job]]></title>
                <link>https://devdojo.com/tinahammar/helper-to-detect-if-laravel-is-running-a-job</link>
                <description><![CDATA[<p>Create a service class</p>
<pre><code class="hljsxml"><span class="php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Services</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppState</span>
</span>{
    <span class="hljs-keyword">public</span> bool $isProcessingJob = <span class="hljs-keyword">false</span>;
}

</span></code></pre>
<p>Add these methods to any ServiceProvider</p>
<pre><code class="hljsphp"><span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Queue</span>\<span class="hljs-title">Events</span>\<span class="hljs-title">JobProcessed</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Queue</span>\<span class="hljs-title">Events</span>\<span class="hljs-title">JobProcessing</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Services</span>\<span class="hljs-title">AppState</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">App</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Queue</span>;


<span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">registerAppStateService</span><span class="hljs-params">()</span>: <span class="hljs-title">void</span>
</span>{
    <span class="hljs-keyword">$this</span>-&gt;app-&gt;singleton(AppState::class, fn (): AppState =&gt; <span class="hljs-keyword">new</span> AppState());
}

<span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">detectQueueState</span><span class="hljs-params">()</span>: <span class="hljs-title">void</span>
</span>{
        Queue::before(<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(JobProcessing $event)</span>: <span class="hljs-title">void</span> </span>{
            app(AppState::class)-&gt;isProcessingJob = <span class="hljs-keyword">true</span>;
        });

        Queue::after(<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(JobProcessed $event)</span>: <span class="hljs-title">void</span> </span>{
            app(AppState::class)-&gt;isProcessingJob = <span class="hljs-keyword">false</span>;
        });

        App::macro(<span class="hljs-string">'isProcessingJob'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span>: <span class="hljs-title">bool</span> </span>{
            <span class="hljs-keyword">return</span> app(AppState::class)-&gt;isProcessingJob;
        });
}
</code></pre>
<p>Add them to the boot() and register() methods of the same service provider, to ensure they get registered.</p>
<pre><code class="hljsphp">
<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-keyword">$this</span>-&gt;registerAppStateService();
}


<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">boot</span><span class="hljs-params">()</span>: <span class="hljs-title">void</span>
</span>{
    <span class="hljs-keyword">$this</span>-&gt;detectQueueState();
}
</code></pre>
<p>Now you can use it anywhere in your Laravel app to evaluate if the code is running inside a job.</p>
<pre><code class="hljsphp"><span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">App</span>;


<span class="hljs-keyword">if</span> (App::isProcessingJob()) {
    <span class="hljs-comment">//running a job</span>
}

<span class="hljs-comment">//or </span>
<span class="hljs-keyword">if</span> (app()-&gt;isProcessingJob()) {
    <span class="hljs-comment">//running a job</span>
}
</code></pre>
<h3 id="other-use-case-for-the-appstate-service-class">Other use case for the AppState service class</h3>
<p><a href="https://devdojo.com/tinahammar/how-to-disable-the-soft-delete-scope-in-laravel-jobs">Conditional Soft Deleting Scope</a></p>
<p><a href="https://laravel.com/docs/10.x/queues#job-events">Related Laravel documentation</a></p>
]]></description>
                                                                    <author><![CDATA[ ]]></author>
                <guid>https://devdojo.com/11790</guid>
                <pubDate>Wed, 15 Nov 2023 09:41:23 -0800</pubDate>
            </item>
                    <item>
                <title><![CDATA[Simplifying Your Video Playback With Laravel]]></title>
                <link>https://devdojo.com/superdev/simplifying-video-playback-with-laravel</link>
                <description><![CDATA[<p>As Laravel developers, we're always on the lookout for ways to enhance the user experience in our applications. Implementing smooth and user-friendly video playback can be a game-changer. In this article, I'll take you through the process of creating a Laravel component for video playback, complete with a loading delay and Plyr.js integration.</p>
<h2 id="step-1-introduction-to-laravel-components">Step 1: Introduction to Laravel Components</h2>
<p>Laravel components are a powerful feature that allows you to encapsulate and reuse parts of your application's user interface. They bring organization and maintainability to your code, ensuring a consistent look and feel throughout your application. In our case, we're going to create a video player component that you can easily integrate into different parts of your Laravel application.</p>
<h2 id="step-2-building-the-video-player-component">Step 2: Building the Video Player Component</h2>
<p>Let's start building our custom video player component. Here's the code for our <code>video-player.blade.php</code>:</p>
<pre><code class="hljsjavascript">@props([<span class="hljs-string">'src'</span>, <span class="hljs-string">'type'</span>])

&lt;div x-data=<span class="hljs-string">"{ isLoading: true }"</span> <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"bg-white rounded-lg shadow-lg overflow-hidden"</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">"relative aspect-video border rounded-md overflow-hidden bg-slate-100"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">x-show</span>=<span class="hljs-string">"isLoading"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"absolute inset-0 flex items-center justify-center bg-slate-800 flex-col gap-y-2 text-secondary"</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">width</span>=<span class="hljs-string">"24"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"24"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"white"</span> <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"2"</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">class</span>=<span class="hljs-string">"h-8 w-8 animate-spin"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M21 12a9 9 0 1 1-6.219-8.56"</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">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-sm mt-1 text-white"</span>&gt;</span>Loading video...<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> <span class="hljs-attr">x-show</span>=<span class="hljs-string">"!isLoading"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"video-player-container"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"absolute inset-y-0 inset-x-0 w-full h-full"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">video</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"player"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-full"</span> <span class="hljs-attr">controls</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">source</span> <span class="hljs-attr">x-bind:src</span>=<span class="hljs-string">"videoSrc"</span> <span class="hljs-attr">x-bind:type</span>=<span class="hljs-string">"videoType"</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">video</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>In this component, we leverage Alpine.js to manage the loading state. The video player is only displayed once the video has finished loading. Additionally, you can specify the video source (<code>src</code>) and type (<code>type</code>) as parameters when using the component.</p>
<h2 id="step-3-adding-a-loading-delay">Step 3: Adding a Loading Delay</h2>
<p>To enhance the user experience, we simulate a loading delay before displaying the video. We achieve this by setting a timeout using the following JavaScript code:</p>
<pre><code class="hljsjavascript"><span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'DOMContentLoaded'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> videoContainer = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'.aspect-video'</span>);
    <span class="hljs-keyword">const</span> player = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'player'</span>);
    <span class="hljs-keyword">const</span> videoPlayerContainer = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'video-player-container'</span>);

    <span class="hljs-comment">// Simulate loading delay</span>
    setTimeout(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">const</span> videoSrc = @json($src);
        <span class="hljs-keyword">const</span> videoType = @json($type);

        player.setAttribute(<span class="hljs-string">'src'</span>, videoSrc);
        player.setAttribute(<span class="hljs-string">'type'</span>, videoType);
        player.load();

        player.addEventListener(<span class="hljs-string">'loadeddata'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
            videoPlayerContainer.style display = <span class="hljs-string">'block'</span>;
            videoContainer.querySelector(<span class="hljs-string">'.text-secondary'</span>).style.display = <span class="hljs-string">'none'</span>;

            <span class="hljs-comment">// Initialize Plyr player for enhanced video controls</span>
            <span class="hljs-keyword">const</span> plyrPlayer = <span class="hljs-keyword">new</span> Plyr(player, {
                <span class="hljs-attr">controls</span>: [
                    <span class="hljs-string">'play-large'</span>,
                    <span class="hljs-string">'restart'</span>,
                    <span class="hljs-string">'rewind'</span>,
                    <span class="hljs-string">'play'</span>,
                    <span class="hljs-string">'fast-forward'</span>,
                    <span class="hljs-string">'progress'</span>,
                    <span class="hljs-string">'current-time'</span>,
                    <span class="hljs-string">'duration'</span>,
                    <span class="hljs-string">'mute'</span>,
                    <span class="hljs-string">'volume'</span>,
                    <span class="hljs-string">'captions'</span>,
                    <span class="hljs-string">'settings'</span>,
                    <span class="hljs-string">'pip'</span>,
                    <span class="hljs-string">'airplay'</span>,
                    <span class="hljs-string">'fullscreen'</span>,
                    <span class="hljs-string">'quality'</span>,
                ],
            });

            <span class="hljs-comment">// Update the isLoading flag to hide the preloader</span>
            Alpine.store(<span class="hljs-string">'isLoading'</span>, <span class="hljs-literal">false</span>);
        });
    }, <span class="hljs-number">2000</span>); <span class="hljs-comment">// Adjust the delay time for an enhanced user experience</span>
});
</code></pre>
<p>This code introduces a two-second delay to enhance user experience, giving a sense of anticipation before the video starts playing.</p>
<h2 id="step-4-integrating-plyr-js-for-enhanced-video-controls">Step 4: Integrating Plyr.js for Enhanced Video Controls</h2>
<p>To provide a feature-rich video playback experience, we integrate the Plyr.js library into our component. Plyr.js offers an array of controls like play, rewind, fast-forward, volume adjustment, captions, and more. Here's how we initialize Plyr.js:</p>
<pre><code class="hljsgo"><span class="hljs-keyword">const</span> plyrPlayer = <span class="hljs-built_in">new</span> Plyr(player, {
    controls: [
        <span class="hljs-string">'play-large'</span>,
        <span class="hljs-string">'restart'</span>,
        <span class="hljs-string">'rewind'</span>,
        <span class="hljs-string">'play'</span>,
        <span class="hljs-string">'fast-forward'</span>,
        <span class="hljs-string">'progress'</span>,
        <span class="hljs-string">'current-time'</span>,
        <span class="hljs-string">'duration'</span>,
        <span class="hljs-string">'mute'</span>,
        <span class="hljs-string">'volume'</span>,
        <span class="hljs-string">'captions'</span>,
        <span class="hljs-string">'settings'</span>,
        <span class="hljs-string">'pip'</span>,
        <span class="hljs-string">'airplay'</span>,
        <span class="hljs-string">'fullscreen'</span>,
        <span class="hljs-string">'quality'</span>,
    ],
});
</code></pre>
<p>With this integration, users can enjoy a seamless video playback experience with a wide range of controls at their fingertips.</p>
<h2 id="step-5-why-this-matters-for-your-laravel-application">Step 5: Why This Matters for Your Laravel Application</h2>
<p>Automating video playback and providing a feature-rich player can significantly enhance the user experience in your Laravel application. It simplifies the implementation of video elements across your views, ensuring a consistent and professional look. Whether you're building an e-learning platform, a video streaming service, or any application that includes video content, this custom video player component can save you time and effort.</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>Benefits of using this custom video player component in your Laravel application:</p>
<ul>
<li>
<p><strong>User-friendly experience</strong>: Users can enjoy a seamless video playback experience with controls like play, rewind, volume adjustment, and more.</p>
</li>
<li>
<p><strong>Consistency</strong>: Using a custom component ensures a consistent look and feel throughout your application, improving user satisfaction.</p>
</li>
<li>
<p><strong>Efficiency</strong>: Instead of having a giant line of code, you have a reusable component instead!</p>
</li>
<li>
<p><strong>Customization</strong>: You can further customize the video player's appearance and behavior to align with your application's design and requirements.</p>
</li>
</ul>
<p>In summary, by incorporating this video player component into your Laravel application, you can provide a user-friendly and consistent video playback experience while saving development time. It's a valuable addition to your toolkit for building applications with video content.</p>
<p>Enhancing user experience is a key aspect of successful application development. The combination of a custom video player component, a loading delay, and Plyr.js integration is a powerful way to achieve this. It streamlines the process of implementing video playback, providing your users with a seamless and enjoyable experience.</p>
<p>In conclusion, creating a seamless video playback experience with Laravel components is a testament to the versatility and power of the Laravel framework. It empowers you to craft beautiful and interactive user interfaces with ease. So, take advantage of these tools and deliver outstanding video content to your users. Happy coding!</p>
]]></description>
                                                            <category>php</category>
                                            <category>laravel</category>
                                            <category>development</category>
                                            <category>devdojo</category>
                                            <category>components</category>
                                            <category>guide</category>
                                            <category>superdev</category>
                                            <category>10</category>
                                            <category>9</category>
                                            <category>2023</category>
                                            <category>10x</category>
                                            <category>9x</category>
                                            <category>l10</category>
                                            <category>l9</category>
                                            <category>convenience</category>
                                                    <author><![CDATA[ ]]></author>
                <guid>https://devdojo.com/11774</guid>
                <pubDate>Fri, 03 Nov 2023 21:35:23 -0700</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[What is Virtual DOM in React Native?]]></title>
                <link>https://devdojo.com/greennolgaa1/what-is-virtual-dom-in-react-native</link>
                <description><![CDATA[<p>In the world of front-end development, React Native has taken the spotlight as one of the most prominent frameworks for building mobile applications. It's known for its efficiency, performance, and, of course, the Virtual DOM. But what exactly is the Virtual DOM, and how does it work in the context of React Native? In this in-depth guide, we'll unravel the mysteries of the Virtual DOM, explore its significance, and understand how it enhances the performance of React Native applications.</p>
<h2 id="understanding-the-dom">Understanding the DOM</h2>
<p>To comprehend the Virtual DOM, we must first grasp the concept of the Document Object Model (DOM). The DOM is a programming interface for web documents. It represents the page so that programs can change the document structure, style, and content dynamically. Essentially, it provides a tree-like structure of elements on a web page, such as HTML tags, and allows developers to manipulate these elements using JavaScript.</p>
<p>In the context of web development, when you make changes to a web page (e.g., updating text, adding or removing elements), you are essentially interacting with the DOM. These interactions can be relatively slow, especially when dealing with complex web applications, because each change to the DOM can trigger a reflow and repaint process, which is resource-intensive.</p>
<h2 id="introducing-the-virtual-dom">Introducing the Virtual DOM</h2>
<p>The Virtual DOM is a concept that was popularized by React (both React Native and React for web). It is a lightweight, in-memory representation of the actual DOM elements in your application. React Native, being built upon the core principles of React, also employs the Virtual DOM to enhance the efficiency and performance of mobile applications.</p>
<p>Here's how the Virtual DOM works in a nutshell:</p>
<ol>
<li>Initial Render: When your React Native application first loads, it creates a Virtual DOM representation of the UI. This Virtual DOM is essentially a JavaScript object that mirrors the structure of the actual UI.</li>
<li>Updating State: When a change occurs in your application (e.g., a button is clicked, a data fetch is complete, or a user input is received), React Native updates the state of the components that need to be re-rendered.</li>
<li>Reconciliation: React Native then performs a process called reconciliation, where it compares the current Virtual DOM with the new Virtual DOM that represents the updated state.</li>
<li>Differential Rendering: During reconciliation, React Native identifies the differences between the two Virtual DOMs, which are often referred to as "diffing." This process is highly efficient because it only focuses on the components that have changed.</li>
<li>Re-rendering: React Native updates only the components that have changed in the actual DOM, minimizing the need for costly reflows and repaints. This selective update of the DOM is a key factor in React Native's speed and efficiency.</li>
</ol>
<h2 id="benefits-of-the-virtual-dom-in-react-native">Benefits of the Virtual DOM in React Native</h2>
<p>Now that we have a basic understanding of how the Virtual DOM operates, let's explore its advantages in the context of React Native development:</p>
<ol>
<li>
<p>Performance Optimization: The Virtual DOM significantly improves the performance of React Native applications by reducing the number of interactions with the actual DOM. Instead of directly manipulating the DOM each time there's a change, React Native works with the lightweight Virtual DOM, which is faster to update.</p>
</li>
<li>
<p>Minimized DOM Manipulation: Directly manipulating the DOM can be slow and resource-intensive. With the Virtual DOM, React Native intelligently calculates the minimal number of changes required to update the actual DOM. This minimizes the impact on performance and leads to a smoother user experience.</p>
</li>
<li>
<p>Reactive Updates: React Native components are designed to be reactive. When the state of a component changes, React Native automatically triggers a re-render of that component and updates the Virtual DOM. This allows for real-time updates in the UI in response to user interactions or data changes.</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>Cross-Platform Consistency: React Native's use of the Virtual DOM ensures a consistent UI experience across different platforms (iOS and Android). The same React Native codebase can render UI elements that look and feel native on both platforms.</p>
</li>
<li>
<p>Developer Productivity: Developers benefit from the Virtual DOM's ease of use. They can focus on building the application's logic and components without worrying too much about the underlying complexities of DOM manipulation.</p>
</li>
<li>
<p>Selective Rendering: The Virtual DOM's differential rendering approach means that only the components affected by a change are updated. This fine-grained control over updates is more efficient than re-rendering the entire UI.</p>
</li>
</ol>
<h2 id="how-to-work-with-the-virtual-dom-in-react-native">How to Work with the Virtual DOM in React Native</h2>
<p>Now that we've established the significance of the <a href="https://www.cronj.com/blog/virtual-dom-react-js/">Virtual DOM in React Native</a>, let's explore how to work with it effectively:</p>
<ol>
<li>
<p>Component-Based Development: In React Native, as in React, development is component-based. Break your UI into reusable components, each responsible for a specific part of the UI. This approach aligns well with the Virtual DOM, which can efficiently update and re-render individual components as needed.</p>
</li>
<li>
<p>State Management: Utilize state management libraries like Redux or Mobx to manage the application's state. These libraries work seamlessly with React Native and help ensure that your application's state changes are properly propagated to the Virtual DOM.</p>
</li>
<li>
<p>Pure Components: React Native provides a PureComponent class that you can use for components that should only re-render when their props or state change. This optimization reduces unnecessary re-renders.</p>
</li>
<li>
<p>Keys for Lists: When rendering lists of elements (e.g., in a FlatList), provide unique key props for each item. This helps React Native's reconciliation algorithm identify items efficiently during updates.</p>
</li>
<li>
<p>Avoid Direct DOM Manipulation: Resist the temptation to manipulate the actual DOM directly. Instead, work with React Native's component state and props, allowing the Virtual DOM to handle updates.</p>
</li>
<li>
<p>Optimize Render Methods: Review your component's render methods to ensure they are efficient and do not contain costly operations. If possible, move heavy computations outside the render method.</p>
</li>
<li>
<p>React DevTools: For debugging and performance profiling, consider using React DevTools. These tools provide insights into your application's component hierarchy and rendering performance, allowing you to optimize where necessary.</p>
</li>
<li>
<p>Throttle or Debounce Events: When dealing with user interactions or data updates that trigger frequent changes, consider using throttling or debouncing techniques to avoid excessive re-renders.</p>
</li>
</ol>
<h2 id="common-misconceptions-about-the-virtual-dom-in-react-native">Common Misconceptions about the Virtual DOM in React Native</h2>
<p>While the Virtual DOM is a powerful concept, there are some common misconceptions that need clarification:</p>
<ol>
<li>
<p>The Virtual DOM Is Specific to React: While the Virtual DOM was popularized by React, it is not exclusive to React. Other libraries and frameworks, including some used in React Native development, also leverage the concept of a Virtual DOM to optimize rendering.</p>
</li>
<li>
<p>The Virtual DOM Is Always Faster: While the Virtual DOM is highly efficient, it doesn't guarantee instant performance gains. The actual impact on performance depends on various factors, including the complexity of your application, how efficiently you write your components, and the quality of your state management.</p>
</li>
<li>
<p>The Virtual DOM Eliminates All Performance Bottlenecks: The Virtual DOM addresses many performance bottlenecks related to frequent DOM updates, but it doesn't solve all performance issues. Other factors, such as network requests, image loading, and heavy computations, can still affect your application's performance.</p>
</li>
<li>
<p>You Shouldn't Worry About Performance with the Virtual DOM: While the Virtual DOM does a lot of heavy lifting for you, it's essential to understand performance optimization techniques and best practices to ensure your React Native application performs well in real-world scenarios.</p>
</li>
</ol>
<h2 id="in-conclusion">In Conclusion</h2>
<p>The Virtual DOM is a cornerstone of React Native's efficiency and performance. It streamlines the process of updating the user interface by intelligently calculating and applying only the necessary changes to the actual DOM. As a React Native developer, understanding the role of the Virtual DOM in your application is crucial for creating high-performance mobile apps.</p>
<p>By embracing component-based development, employing effective state management, and following best practices, you can harness the power of the Virtual DOM to deliver smooth and responsive user experiences in your React Native applications. As you continue your journey in React Native development, remember that the Virtual DOM is your ally in achieving optimal performance and maintaining a consistent and dynamic user interface.</p>
]]></description>
                                                            <category>web development</category>
                                            <category>react</category>
                                            <category>react native</category>
                                            <category>virtual dom</category>
                                                    <author><![CDATA[Sam Smithh]]></author>
                <guid>https://devdojo.com/11265</guid>
                <pubDate>Wed, 23 Aug 2023 01:47:23 -0700</pubDate>
            </item>
            </channel>
</rss>
