<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gurinder Chauhan</title>
    <description>The latest articles on DEV Community by Gurinder Chauhan (@gurinderchauhan).</description>
    <link>https://dev.to/gurinderchauhan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F414647%2Fd5fd88c7-000e-42e1-9169-06e05e91c8c3.png</url>
      <title>DEV Community: Gurinder Chauhan</title>
      <link>https://dev.to/gurinderchauhan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gurinderchauhan"/>
    <language>en</language>
    <item>
      <title>I Connected My AI Agent to My Inbox with MCP - Here's How It Works</title>
      <dc:creator>Gurinder Chauhan</dc:creator>
      <pubDate>Fri, 18 Sep 2026 21:47:20 +0000</pubDate>
      <link>https://dev.to/gurinderchauhan/i-connected-my-ai-agent-to-my-inbox-with-mcp-heres-how-it-works-2g28</link>
      <guid>https://dev.to/gurinderchauhan/i-connected-my-ai-agent-to-my-inbox-with-mcp-heres-how-it-works-2g28</guid>
      <description>&lt;p&gt;Consider a concrete task: triaging a support inbox. An assistant with mailbox access can search the mailbox, summarize the threads, and leave drafts waiting for review. No integration code, no API glue. Just a server URL and a sign-in.&lt;/p&gt;

&lt;p&gt;That server is the new &lt;a href="https://letmesend.email/docs/mcp/overview" rel="noopener noreferrer"&gt;LetMeSend.Email MCP server&lt;/a&gt;, and this post is the practical walkthrough: what it exposes, how auth works, how to connect it, and the workflows I've found genuinely useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the server exposes
&lt;/h2&gt;

&lt;p&gt;One Streamable HTTP endpoint - &lt;code&gt;https://mcp.letmesend.email&lt;/code&gt; - serves &lt;strong&gt;35 tools&lt;/strong&gt; across four areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mailboxes&lt;/strong&gt; (&lt;code&gt;mailboxes-list&lt;/code&gt;, &lt;code&gt;mailbox-messages-search&lt;/code&gt;, &lt;code&gt;mailbox-messages-thread&lt;/code&gt;, &lt;code&gt;mailbox-messages-reply&lt;/code&gt;, &lt;code&gt;mailbox-messages-action&lt;/code&gt;, &lt;code&gt;mailbox-drafts-create&lt;/code&gt;, …) - read, search, thread, reply, forward, archive, and draft.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transactional email&lt;/strong&gt; (&lt;code&gt;emails-send&lt;/code&gt;, &lt;code&gt;emails-list&lt;/code&gt;, &lt;code&gt;emails-get&lt;/code&gt;) - send through verified domains, inspect past sends.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domains&lt;/strong&gt; (&lt;code&gt;domains-list&lt;/code&gt;, &lt;code&gt;domains-verify&lt;/code&gt;, &lt;code&gt;domain-health-check&lt;/code&gt;, &lt;code&gt;domain-health-history&lt;/code&gt;, &lt;code&gt;email-verification-check&lt;/code&gt;) - DNS verification, deliverability health and history, single-address validation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Marketing&lt;/strong&gt; (&lt;code&gt;contacts-create&lt;/code&gt;, &lt;code&gt;contacts-list&lt;/code&gt;, &lt;code&gt;campaigns-create&lt;/code&gt;, &lt;code&gt;campaigns-schedule&lt;/code&gt;, &lt;code&gt;campaigns-send&lt;/code&gt;, &lt;code&gt;campaigns-cancel&lt;/code&gt;) - contacts and full campaign lifecycle.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every call runs under the same rules as the dashboard: the agent only sees mailboxes assigned to it, scoped to ability tiers (&lt;code&gt;mailbox:read&lt;/code&gt;, &lt;code&gt;mailbox:write&lt;/code&gt;, &lt;code&gt;mailbox:send&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication: OAuth first, API key for headless
&lt;/h2&gt;

&lt;p&gt;The recommended path is OAuth - the client opens a browser sign-in where you log in with your LetMeSend.Email account and approve the mailbox abilities the agent gets. Nothing to copy into config files.&lt;/p&gt;

&lt;p&gt;For headless setups (CI, servers, cron jobs), create a dashboard API key and send it as a Bearer token instead. Same tools, same guardrails, no browser involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup with OpenCode
&lt;/h2&gt;

&lt;p&gt;This is the flow from the &lt;a href="https://letmesend.email/docs/mcp/overview" rel="noopener noreferrer"&gt;OpenCode setup guide&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opencode mcp add
&lt;span class="c"&gt;# Choose remote, name it lmse, and enter https://mcp.letmesend.email&lt;/span&gt;
opencode mcp auth lmse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;auth&lt;/code&gt; command walks you through sign-in, including picking mailbox ability tiers. Then confirm the connection with a first prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;List my mailboxes using the LetMeSend.Email tools.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If it answers with your real mailboxes (that's &lt;code&gt;mailboxes-list&lt;/code&gt; under the hood), you're connected. Prerequisites: a LetMeSend.Email account, and a verified domain before anything is allowed to send. The same pattern - remote URL plus OAuth or Bearer - applies to the other documented clients: Claude, Cursor, Copilot, Codex, Gemini, Warp, Zed, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workflow 1: Summarize threads, draft follow-ups
&lt;/h2&gt;

&lt;p&gt;A useful bounded task for an agent:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Search the support mailbox for threads from the last two days, summarize each one, and draft a follow-up message for each - don't send anything.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent chains &lt;code&gt;mailbox-messages-search&lt;/code&gt; → &lt;code&gt;mailbox-messages-thread&lt;/code&gt; → &lt;code&gt;mailbox-drafts-create&lt;/code&gt;. One permission note: searching and summarizing run fine on &lt;code&gt;mailbox:read&lt;/code&gt;, but creating drafts requires write access - so this workflow needs the agent to hold both. Two things to know about that last step: &lt;code&gt;mailbox-drafts-create&lt;/code&gt; saves a standalone draft (recipients, subject, body) - it takes no thread reference, so treat its output as a follow-up draft to review, not an in-thread reply. Actual in-thread replies go through &lt;code&gt;mailbox-messages-reply&lt;/code&gt;, which sends immediately from the mailbox's primary address - only invoke that one with explicit approval. The boundary is structural: &lt;strong&gt;creating a draft and sending an email are different tools&lt;/strong&gt;, so "don't send anything" means there's simply no send call in the chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workflow 2: Check domain health in one question
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Is example.com healthy? Check DNS, read the current deliverability score, and flag anything I should fix.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Behind that sentence: &lt;code&gt;domains-verify&lt;/code&gt; for the DNS records, &lt;code&gt;domain-health-get&lt;/code&gt; for the current score with findings and recommendations, &lt;code&gt;domain-health-history&lt;/code&gt; for the trend. (&lt;code&gt;domain-health-check&lt;/code&gt; triggers a fresh scan instead - it's rate-limited, so reach for it only when you need new data, not for routine reads.) What used to be several dashboard tabs and some DNS-tab archaeology becomes a single answer with a fix list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workflow 3: Prepare a campaign, review before it flies
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Create a draft campaign for Friday's newsletter. Show me its audience and content for review. Don't schedule or send it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;campaigns-create&lt;/code&gt; stages the campaign and stops there. Note the deliberate omission: scheduling authorizes a future send, so it belongs after review, not before it. Once you've approved the draft, scheduling (&lt;code&gt;campaigns-schedule&lt;/code&gt;) and sending (&lt;code&gt;campaigns-send&lt;/code&gt;) are separate explicit steps. The pattern across all three workflows is the same: the agent does the assembly, you keep the trigger.&lt;/p&gt;

&lt;h2&gt;
  
  
  The draft/send boundary, stated plainly
&lt;/h2&gt;

&lt;p&gt;Worth repeating because it's the whole safety model: draft creation does not send; sending tools deliver immediately, while scheduling authorizes a future send. Start new agents on &lt;code&gt;mailbox:read&lt;/code&gt; - noting that read still permits organizing actions like archiving - and graduate to send access once you've seen the drafts it produces.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next, and where to start
&lt;/h2&gt;

&lt;p&gt;On the roadmap (planned, not shipped): a short walkthrough video of the server end-to-end, and an n8n plugin for workflow automation. I'll update this post when they land.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://letmesend.email/blog/introducing-the-letmesend-email-mcp-server-give-your-ai-agents-the-power-to-send-email" rel="noopener noreferrer"&gt;Read announcement blog post&lt;/a&gt; to learn more about MCP server.&lt;/p&gt;

&lt;p&gt;To try it now: create an account, verify a domain, point your agent at &lt;code&gt;https://mcp.letmesend.email&lt;/code&gt;, and start with "list my mailboxes." Full reference lives at &lt;a href="https://letmesend.email/docs/mcp/overview" rel="noopener noreferrer"&gt;letmesend.email/docs/mcp/overview&lt;/a&gt; - and if you wire up a workflow I haven't thought of, I'd genuinely like to hear about it in the comments.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>agentskills</category>
      <category>ai</category>
    </item>
    <item>
      <title>Laravel 13.20 Image Processing: A Production Guide</title>
      <dc:creator>Gurinder Chauhan</dc:creator>
      <pubDate>Wed, 15 Jul 2026 22:26:19 +0000</pubDate>
      <link>https://dev.to/gurinderchauhan/laravel-1320-image-processing-a-production-guide-381n</link>
      <guid>https://dev.to/gurinderchauhan/laravel-1320-image-processing-a-production-guide-381n</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally published on &lt;a href="https://letmesend.email/development/laravel-13-20-image-facade-the-complete-guide" rel="noopener noreferrer"&gt;letmesend.email&lt;/a&gt;. I work on letmesend.email, and this is an adapted edition of that guide.&lt;/p&gt;

&lt;p&gt;This article was prepared with AI-assisted editorial support. Its technical claims and examples were tested against Laravel Framework v13.20.0 in an isolated application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Image handling rarely stays small. An avatar upload becomes a thumbnail, a WebP copy, a storage policy, a validation rule, and eventually a queue job.&lt;/p&gt;

&lt;p&gt;Laravel 13.20, released on July 14, 2026, gives this work a first-party home through the new &lt;code&gt;Illuminate\Image&lt;/code&gt; component and &lt;code&gt;Image&lt;/code&gt; facade. It connects image processing to requests, filesystem disks, configuration, and Laravel's container while keeping transformation pipelines immutable.&lt;/p&gt;

&lt;p&gt;This guide focuses on what actually shipped and the production boundaries that still belong in your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  What first-party image processing means
&lt;/h2&gt;

&lt;p&gt;Laravel applications have used Intervention Image, custom wrappers, and external image services for years. Laravel 13.20 does not make Intervention Image disappear. The built-in GD and Imagick drivers use &lt;code&gt;intervention/image:^4.0&lt;/code&gt; underneath.&lt;/p&gt;

&lt;p&gt;The first-party part is the Laravel-facing API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Image&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$thumbnail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;storage_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app/source/photo.jpg'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;orient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;640&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toWebp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$storedPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$thumbnail&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'thumbnails'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'public'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$storedPath&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'The thumbnail could not be stored.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That chain describes four concerns:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Load the source lazily.&lt;/li&gt;
&lt;li&gt;Record transformations in order.&lt;/li&gt;
&lt;li&gt;Select an output format and quality.&lt;/li&gt;
&lt;li&gt;Process and store the result through Laravel's filesystem.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The built-in drivers support GD and Imagick. GD is the default. The default processing quality is 70 unless the pipeline selects another value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install and configure a driver
&lt;/h2&gt;

&lt;p&gt;Laravel 13.20 and the image component require PHP 8.3 or newer. Install the suggested processing dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require intervention/image:^4.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your runtime also needs the extension for the chosen driver:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GD requires &lt;code&gt;ext-gd&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Imagick requires &lt;code&gt;ext-imagick&lt;/code&gt; and the ImageMagick system library.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The image configuration uses GD by default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'default'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'IMAGE_DRIVER'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'gd'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use Imagick as the application default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;IMAGE_DRIVER&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;imagick&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An individual pipeline can choose a different driver without changing global configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Image&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;storage_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app/source/photo.jpg'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nv"&gt;$gdVariant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$source&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;usingGd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$imagickVariant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$source&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;usingImagick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test both drivers with the same representative files in the container you deploy. Output size, memory use, and processing time depend on the source material and extension build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create images from supported sources
&lt;/h2&gt;

&lt;p&gt;The component accepts request uploads, local paths, URLs, raw bytes, Base64 strings, and Laravel storage paths.&lt;/p&gt;

&lt;h3&gt;
  
  
  Request uploads
&lt;/h3&gt;

&lt;p&gt;Validate an upload before asking the image driver to decode it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Http\Request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'avatar'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'required'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'image'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'mimes:jpeg,png,webp'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'max:5120'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'dimensions:min_width=200,min_height=200,max_width=6000,max_height=6000'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nv"&gt;$image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'avatar'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;abort_if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$image&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;422&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'A valid avatar image is required.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Build the output pipeline from $image.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;$request-&amp;gt;image('avatar')&lt;/code&gt; returns an &lt;code&gt;Illuminate\Image\Image&lt;/code&gt; when the input contains one uploaded file. It returns &lt;code&gt;null&lt;/code&gt; when it does not.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other entry points
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Image&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Storage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$binaryContents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;file_get_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;storage_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app/source/photo.jpg'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$binaryContents&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'The source image could not be read.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$base64Contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;base64_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$binaryContents&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$fromPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;storage_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app/source/photo.jpg'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nv"&gt;$fromUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://assets.example.test/photo.jpg'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$fromBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$binaryContents&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$fromBase64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromBase64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$base64Contents&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$fromStorage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromStorage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'originals/photo.jpg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'s3'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$fromDisk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Storage&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;disk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'s3'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'originals/photo.jpg'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most sources are lazy. Laravel reads or downloads the actual contents when output or metadata is requested.&lt;/p&gt;

&lt;p&gt;Do not pass an untrusted user URL directly to &lt;code&gt;fromUrl()&lt;/code&gt;. A controlled downloader should limit redirects and response size, reject private network destinations, set a short timeout, and confirm the media type first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Immutability changes how variants are built
&lt;/h2&gt;

&lt;p&gt;Every transformation and output option clones the image. The source pipeline does not change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Image&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;storage_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app/source/product.jpg'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;orient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$thumbnail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$source&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toWebp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$detail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$source&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1600&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toWebp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;84&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$openGraph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$source&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;630&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toJpg&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes branching predictable. Adding &lt;code&gt;cover()&lt;/code&gt; to &lt;code&gt;$thumbnail&lt;/code&gt; cannot accidentally resize &lt;code&gt;$detail&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There is one related boundary: an &lt;code&gt;Image&lt;/code&gt; object cannot be serialized. Do not put one on a queued job. Store the original first, then dispatch the storage path or your own asset identifier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose the transformation that matches the layout
&lt;/h2&gt;

&lt;p&gt;Laravel 13.20 exposes these transformations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Use it when&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cover()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The output must fill exact dimensions and cropping is acceptable.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contain()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The complete image must fit inside a box without cropping.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;crop()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You need a specific rectangular region.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;resize()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You intentionally want exact dimensions, even if the aspect ratio changes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scale()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You want proportional resizing from one target dimension.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rotate()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The application needs an explicit rotation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;orient()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Uploaded photos should respect orientation metadata.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;blur()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You need a blur effect or low-resolution placeholder.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sharpen()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A resized output needs measured sharpening.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grayscale()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The output should be converted to grayscale.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;flipVertically()&lt;/code&gt; or &lt;code&gt;flip()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;The image should be flipped vertically.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;flipHorizontally()&lt;/code&gt; or &lt;code&gt;flop()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;The image should be flipped horizontally.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Prefer &lt;code&gt;cover()&lt;/code&gt; for fixed cards and avatars, &lt;code&gt;contain()&lt;/code&gt; when the whole product or logo must remain visible, and &lt;code&gt;scale()&lt;/code&gt; for responsive width variants.&lt;/p&gt;

&lt;h2&gt;
  
  
  Convert and inspect output
&lt;/h2&gt;

&lt;p&gt;Laravel 13.20 provides explicit WebP and JPEG output methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Image&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;storage_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app/source/product.jpg'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nv"&gt;$webp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$source&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toWebp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;82&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;optimize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$jpeg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$source&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;630&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toJpg&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;toJpeg()&lt;/code&gt; is also available as an alias for &lt;code&gt;toJpg()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Using the &lt;code&gt;$webp&lt;/code&gt; pipeline created above, you can inspect or export processed output with methods such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$webp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;width&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$webp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;height&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$dimensions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$webp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;dimensions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$mimeType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$webp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;mimeType&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$extension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$webp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$webp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toBytes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$base64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$webp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toBase64&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$dataUri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$webp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toDataUri&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Store paths and image metadata in the database, not the &lt;code&gt;Image&lt;/code&gt; instance or a large Base64 value.&lt;/p&gt;

&lt;h2&gt;
  
  
  A production avatar workflow
&lt;/h2&gt;

&lt;p&gt;A safe workflow preserves the previous asset until both the new file and database update succeed. The following shortened example shows the important boundary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Http\Request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Illuminate\Support\Facades\DB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Storage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Str&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;updateAvatar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'avatar'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'required'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'image'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'mimes:jpeg,png,webp'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'max:5120'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'dimensions:min_width=200,min_height=200,max_width=6000,max_height=6000'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nv"&gt;$source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'avatar'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;abort_if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$source&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;422&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'A valid avatar image is required.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;abort_if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"avatars/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'.webp'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;$stored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$source&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;orient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toWebp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;82&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;storeAs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;disk&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'public'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$stored&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'The avatar could not be stored.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;forceFill&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'avatar_path'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Storage&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;disk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'public'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;back&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'status'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Avatar updated.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a full replacement workflow, keep the old path before the transaction and delete it only after the new path is active. Versioned filenames prevent a failed write from destroying the last known good asset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Move expensive work to a queue
&lt;/h2&gt;

&lt;p&gt;Small avatars may be reasonable to process during a request. Large originals, many variants, and batch imports usually belong on a queue.&lt;/p&gt;

&lt;p&gt;A reliable sequence is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Validate and store the original upload.&lt;/li&gt;
&lt;li&gt;Create an asset record with a pending state.&lt;/li&gt;
&lt;li&gt;Dispatch a job with the asset identifier or storage path.&lt;/li&gt;
&lt;li&gt;Load the source from storage in the job.&lt;/li&gt;
&lt;li&gt;Generate every required variant.&lt;/li&gt;
&lt;li&gt;Mark the asset ready only after all required writes succeed.&lt;/li&gt;
&lt;li&gt;Record failures and allow controlled retries.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Keep retry operations idempotent. A job should produce the same variant paths or replace them through an explicit versioning policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production checks worth keeping
&lt;/h2&gt;

&lt;p&gt;The convenient facade does not remove the operational work around image processing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate MIME type, compressed size, and pixel dimensions before decoding.&lt;/li&gt;
&lt;li&gt;Decide whether animated input is allowed.&lt;/li&gt;
&lt;li&gt;Set web server, PHP, worker memory, and worker timeout limits.&lt;/li&gt;
&lt;li&gt;Cap the number of generated variants.&lt;/li&gt;
&lt;li&gt;Re-encode accepted input instead of keeping an uploaded file as the public derivative.&lt;/li&gt;
&lt;li&gt;Check every storage return value because storage methods return &lt;code&gt;string|false&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Monitor processing time, failures, and worker memory.&lt;/li&gt;
&lt;li&gt;Clean abandoned originals and partial variants.&lt;/li&gt;
&lt;li&gt;Store final width, height, MIME type, and path with each asset variant.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A small compressed file can expand into a large in-memory bitmap. Test the limits with real source dimensions and formats in your production container.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should an existing application migrate?
&lt;/h2&gt;

&lt;p&gt;The new API does not require an immediate rewrite. Start with one contained workflow, such as avatars or product thumbnails, then compare the old and new output:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Crop and scale behavior&lt;/li&gt;
&lt;li&gt;EXIF orientation&lt;/li&gt;
&lt;li&gt;Output dimensions&lt;/li&gt;
&lt;li&gt;MIME type and extension&lt;/li&gt;
&lt;li&gt;File size at the selected quality&lt;/li&gt;
&lt;li&gt;Transparency and background behavior&lt;/li&gt;
&lt;li&gt;Animated input behavior, if supported by the product&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the application depends on Intervention operations that Laravel's smaller API does not expose, keep direct Intervention usage for that workflow. If an external image CDN handles dynamic delivery and caching, Laravel's local component does not automatically replace it.&lt;/p&gt;

&lt;p&gt;Laravel 13.20 makes the common path more consistent. It does not eliminate the need for input policy, storage design, queues, observability, or realistic performance testing.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://letmesend.email/development/laravel-13-20-image-facade-the-complete-guide" rel="noopener noreferrer"&gt;complete Laravel 13.20 Image facade guide&lt;/a&gt; includes every released source, transformation, storage method, a multi-variant avatar controller, responsive image markup, migration guidance, tests, and the exact implementation details verified against the v13.20.0 tag.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/laravel/framework/releases/tag/v13.20.0" rel="noopener noreferrer"&gt;Laravel Framework v13.20.0 release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/laravel/framework/pull/59276" rel="noopener noreferrer"&gt;First-party image processing pull request&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://laravel.com/docs/13.x/images" rel="noopener noreferrer"&gt;Laravel 13.x image documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/laravel/framework/blob/v13.20.0/src/Illuminate/Image/Image.php" rel="noopener noreferrer"&gt;Released Image class&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>🚀 How I Stopped Juggling 4 Terminal Tabs Every Morning</title>
      <dc:creator>Gurinder Chauhan</dc:creator>
      <pubDate>Thu, 16 Oct 2025 19:56:12 +0000</pubDate>
      <link>https://dev.to/gurinderchauhan/how-i-stopped-juggling-4-terminal-tabs-every-morning-1dl7</link>
      <guid>https://dev.to/gurinderchauhan/how-i-stopped-juggling-4-terminal-tabs-every-morning-1dl7</guid>
      <description>&lt;p&gt;When working locally on my Laravel + Bun + Stripe stack for &lt;strong&gt;&lt;a href="https://emailbuilder.dev/" rel="noopener noreferrer"&gt;AI Email Builder&lt;/a&gt;&lt;/strong&gt;, I used to open 4 terminal tabs every time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;bun run dev for frontend&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;php artisan queue:work&lt;/code&gt; for jobs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;php artisan schedule:work&lt;/code&gt; for cron&lt;/li&gt;
&lt;li&gt;and Stripe listener for webhooks&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It worked… but it was annoying.&lt;/p&gt;

&lt;p&gt;So I wrote a tiny Bash script to spin everything up in one command 👇&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nv"&gt;BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;

&lt;span class="nv"&gt;DEV_CMD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"(cd &lt;/span&gt;&lt;span class="nv"&gt;$BASE&lt;/span&gt;&lt;span class="s2"&gt; &amp;amp;&amp;amp; bun run dev)"&lt;/span&gt;
&lt;span class="nv"&gt;QUEUE_CMD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"(cd &lt;/span&gt;&lt;span class="nv"&gt;$BASE&lt;/span&gt;&lt;span class="s2"&gt; &amp;amp;&amp;amp; php artisan queue:work)"&lt;/span&gt;
&lt;span class="nv"&gt;SCHEDULE_CMD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"(cd &lt;/span&gt;&lt;span class="nv"&gt;$BASE&lt;/span&gt;&lt;span class="s2"&gt; &amp;amp;&amp;amp; php artisan schedule:work)"&lt;/span&gt;
&lt;span class="nv"&gt;STRIPE_CMD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"(cd &lt;/span&gt;&lt;span class="nv"&gt;$BASE&lt;/span&gt;&lt;span class="s2"&gt; &amp;amp;&amp;amp; zsh listen-stripe.sh)"&lt;/span&gt;

npx concurrently &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-n&lt;/span&gt; dev,queue,schedule,stripe &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-c&lt;/span&gt; green,blue,magenta,yellow &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEV_CMD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$QUEUE_CMD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCHEDULE_CMD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$STRIPE_CMD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now I just run: &lt;code&gt;./dev.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And everything starts together — clean, color-coded, and fast 🙌&lt;/p&gt;

&lt;p&gt;This little script made my daily development workflow for Email Builder so much smoother.&lt;/p&gt;

&lt;p&gt;Feel free to tweak it to match your stack too.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>bunjs</category>
      <category>automation</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
