<?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: Jerome Thayananthajothy</title>
    <description>The latest articles on DEV Community by Jerome Thayananthajothy (@thavarshan).</description>
    <link>https://dev.to/thavarshan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1439901%2F38690798-5247-4b0a-a052-395ceecc591d.jpg</url>
      <title>DEV Community: Jerome Thayananthajothy</title>
      <link>https://dev.to/thavarshan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thavarshan"/>
    <language>en</language>
    <item>
      <title>Introducing Honeymelon: A Case Study in Building a Better Media Converter</title>
      <dc:creator>Jerome Thayananthajothy</dc:creator>
      <pubDate>Fri, 06 Feb 2026 05:50:22 +0000</pubDate>
      <link>https://dev.to/thavarshan/introducing-honeymelon-a-case-study-in-building-a-better-media-converter-51d9</link>
      <guid>https://dev.to/thavarshan/introducing-honeymelon-a-case-study-in-building-a-better-media-converter-51d9</guid>
      <description>&lt;h2&gt;
  
  
  The Problem That Started It All
&lt;/h2&gt;

&lt;p&gt;A few years ago I found myself doing the same thing over and over: converting video and audio files on my Mac so they would play nicely in editors, browsers, or on devices that refused to cooperate with whatever codec I had thrown at them. I was tired of hunting for the right &lt;code&gt;ffmpeg&lt;/code&gt; flags every time, so I did what any developer would do - I built a desktop app to handle it for me.&lt;/p&gt;

&lt;p&gt;That first version was an Electron app. It worked. It solved my problem. But it also consumed an unreasonable amount of memory, shipped a bloated binary, and felt sluggish on a machine that should have breezed through simple media tasks. Every time I opened it I winced at the Activity Monitor numbers. Something had to change.&lt;/p&gt;

&lt;p&gt;Honeymelon is that change.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Honeymelon Actually Is
&lt;/h2&gt;

&lt;p&gt;Honeymelon is a free, open-source media converter built exclusively for macOS on Apple Silicon. It wraps FFmpeg in a clean, native interface and makes one bet that pays off more often than you would expect: &lt;strong&gt;most conversions do not need re-encoding at all.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The core idea is called "remux-first." Before Honeymelon touches a single frame it probes your file, checks whether the existing streams are already compatible with the target container, and — if they are — simply repackages them without decoding or encoding. The result is a conversion that finishes in seconds instead of minutes, with zero quality loss.&lt;/p&gt;

&lt;p&gt;When transcoding is genuinely required, Honeymelon leans on Apple VideoToolbox for hardware-accelerated H.264, HEVC, and ProRes encoding, and offers three quality tiers — Fast, Balanced, and High — so you can decide where you want to land on the speed-versus-quality spectrum.&lt;/p&gt;

&lt;p&gt;Everything happens locally. There is no telemetry, no network traffic, no account to create. You drag files in, pick a preset, and get your output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Rebuilt From Scratch
&lt;/h2&gt;

&lt;p&gt;The Electron version taught me two things. First, wrapping a full Chromium browser inside a desktop app carries a cost that is hard to justify for a utility that should feel lightweight. Second, user experience matters as much as functionality - if the tool feels heavy, people stop reaching for it even when it works.&lt;/p&gt;

&lt;p&gt;I chose Tauri 2 with a Vue 3 frontend and a Rust backend for the rewrite. The difference was immediate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Application size&lt;/strong&gt; dropped dramatically. The distributed DMG is a fraction of what the Electron build weighed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory usage&lt;/strong&gt; fell to levels you would expect from a native macOS utility rather than a web browser pretending to be one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Startup time&lt;/strong&gt; became nearly instant. The Rust backend initializes FFmpeg capability detection in the background while the UI renders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Responsiveness&lt;/strong&gt; improved across the board. Tauri's IPC layer and Rust's async runtime handle concurrent FFmpeg processes without the overhead of a Node.js event loop marshalling everything through a single thread.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choosing Rust for the backend was not just about performance. It brought real safety guarantees — no null pointer panics crashing your conversion mid-job, proper error propagation through typed &lt;code&gt;Result&lt;/code&gt; values, and memory safety without a garbage collector pause interrupting progress events.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture: Probe, Plan, Execute
&lt;/h2&gt;

&lt;p&gt;Under the hood, every conversion passes through three stages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 1 — Probe
&lt;/h3&gt;

&lt;p&gt;Honeymelon invokes &lt;code&gt;ffprobe&lt;/code&gt; to extract detailed metadata from the input file: codecs, container format, duration, resolution, frame rate, color primaries, transfer characteristics, and subtitle types. This data feeds directly into the planning stage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 2 — Plan
&lt;/h3&gt;

&lt;p&gt;A pure TypeScript planning engine evaluates the probe results against the selected preset. It checks container compatibility rules - for example, MP4 only supports certain video and audio codecs - and decides stream by stream whether to copy or transcode. The planner emits a complete set of FFmpeg arguments and a human-readable summary of what it intends to do, along with any warnings.&lt;/p&gt;

&lt;p&gt;The planning logic lives entirely on the frontend, which means it can run instantly without a round-trip to the Rust backend. You see the decision before you commit to it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 3 — Execute
&lt;/h3&gt;

&lt;p&gt;The Rust backend spawns FFmpeg as a child process, parses its stderr output for progress metrics in a dedicated thread, and streams real-time updates - time processed, frames per second, encoding speed, estimated time remaining - back to the Vue UI through Tauri events.&lt;/p&gt;

&lt;p&gt;Concurrency is managed carefully. You can run multiple jobs in parallel, but resource-intensive codecs like AV1 and ProRes automatically trigger exclusive mode, preventing other heavy jobs from starting until they complete. Output files are written to a temporary path first and atomically renamed on success, so a crash or cancellation never leaves behind a half-written file.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Case Study in Trade-Offs
&lt;/h2&gt;

&lt;p&gt;Building Honeymelon meant making deliberate trade-offs, and I think the decisions are worth examining.&lt;/p&gt;

&lt;h3&gt;
  
  
  Platform scope
&lt;/h3&gt;

&lt;p&gt;Honeymelon targets macOS on Apple Silicon exclusively. No Windows, no Linux, no Intel Macs. This is a genuine constraint, and I understand it limits the audience. But it also means the app can lean fully into VideoToolbox hardware acceleration, declare native ARM64 architecture in its bundle metadata, and avoid the cross-platform compromises that dilute so many desktop tools. The result is an application that feels like it belongs on your Mac because it was built for nothing else.&lt;/p&gt;

&lt;h3&gt;
  
  
  LGPL compliance through process separation
&lt;/h3&gt;

&lt;p&gt;FFmpeg is licensed under the LGPL. Rather than linking against its libraries — which would impose LGPL obligations on Honeymelon's own code — the app runs FFmpeg as a completely separate process. Communication happens exclusively through command-line arguments, standard streams, and the file system. No shared memory, no dynamic linking, no library calls. This keeps the licensing clean and lets Honeymelon ship under the GPL v3 without conflict.&lt;/p&gt;

&lt;h3&gt;
  
  
  Privacy as a default
&lt;/h3&gt;

&lt;p&gt;There is no analytics SDK, no crash reporter phoning home, no update check pinging a server. I built this for myself first, and I do not want my media converter talking to the internet. That philosophy extends to every user. If you are working with sensitive footage or operating in an air-gapped environment, Honeymelon will never surprise you with an outbound connection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Free software, full stop
&lt;/h3&gt;

&lt;p&gt;Honeymelon is licensed under the GNU General Public License v3.0 or later. It is not "free tier with upsells." It is not "open core with a proprietary edition." It is free software in the fullest sense — free to use, free to study, free to modify, free to distribute.&lt;/p&gt;

&lt;p&gt;I built this app to solve my own problem. Once it was working, the only honest thing to do was share it. Media conversion is a universal need. Charging for a wrapper around FFmpeg never sat right with me. I believe tools like this should benefit everyone, and profit was never the goal.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;If there is a single takeaway from this project, it is this: &lt;strong&gt;the tools you choose determine the experience you can deliver.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Electron gave me rapid iteration and a familiar web stack, but it imposed a floor on resource consumption that no amount of optimization could lower. Tauri and Rust removed that floor. The same features — concurrent job management, real-time progress tracking, subtitle handling, color metadata preservation — now run in a package that feels native because it is native.&lt;/p&gt;

&lt;p&gt;The second lesson is that &lt;strong&gt;simplicity is a feature.&lt;/strong&gt; The remux-first strategy is not technically impressive. It is a straightforward check: does the source codec match the target? If yes, copy the stream. But that simple check saves users enormous amounts of time and preserves quality they would otherwise lose to unnecessary transcoding. The best engineering often looks obvious in retrospect.&lt;/p&gt;

&lt;p&gt;The third lesson is personal. &lt;strong&gt;Building for yourself is a legitimate motivation.&lt;/strong&gt; Not every project needs a market analysis or a competitive landscape. Sometimes you just need a tool that does one thing well and does not waste your time or your machine's resources doing it. If other people find it useful, that is a bonus — but the software justifies itself the moment it solves your problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Honeymelon is available now on GitHub under the GPL v3.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repository&lt;/strong&gt;: &lt;a href="https://github.com/honeymelon-app/honeymelon" rel="noopener noreferrer"&gt;github.com/honeymelon-app/honeymelon&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Download&lt;/strong&gt;: Grab the latest signed DMG from the &lt;a href="https://github.com/honeymelon-app/honeymelon/releases" rel="noopener noreferrer"&gt;Releases&lt;/a&gt; page&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Requirements&lt;/strong&gt;: macOS 13 or later on Apple Silicon (M1, M2, M3, M4)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To build from source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/honeymelon-app/honeymelon.git
&lt;span class="nb"&gt;cd &lt;/span&gt;honeymelon
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run tauri dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Contributions are welcome. Whether it is a bug report, a new preset, a documentation fix, or a feature idea - the project is open and the issue tracker is waiting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Honeymelon exists because I needed it. It is free because I believe it should be. And it is open source because the best tools get better when other people can see how they work, point out where they fall short, and help make them stronger.&lt;/p&gt;

&lt;p&gt;If you convert media files on a Mac, give it a try. If you are a developer curious about Tauri, Rust, or Vue 3, dig into the source. And if you have feedback, I would genuinely like to hear it.&lt;/p&gt;

</description>
      <category>tauri</category>
      <category>rust</category>
      <category>vue</category>
      <category>ffmpeg</category>
    </item>
    <item>
      <title>Building Altus 4: Why I Created an AI-Enhanced MySQL Search Engine (Instead of Just Using Elasticsearch)</title>
      <dc:creator>Jerome Thayananthajothy</dc:creator>
      <pubDate>Thu, 04 Sep 2025 16:27:43 +0000</pubDate>
      <link>https://dev.to/thavarshan/building-altus-4-why-i-created-an-ai-enhanced-mysql-search-engine-instead-of-just-using-178p</link>
      <guid>https://dev.to/thavarshan/building-altus-4-why-i-created-an-ai-enhanced-mysql-search-engine-instead-of-just-using-178p</guid>
      <description>&lt;p&gt;&lt;em&gt;A case study in solving the "search problem" without forcing infrastructure migrations&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem That Started It All
&lt;/h2&gt;

&lt;p&gt;Picture this: You're three months into building a SaaS application. Your users love it, but they keep asking for one thing; better search functionality. Your current MySQL &lt;code&gt;LIKE&lt;/code&gt; queries are slow, inflexible, and frankly embarrassing when users type "find my recent premium orders" and get zero results.&lt;/p&gt;

&lt;p&gt;Sound familiar? This exact scenario has played out in every project I've worked on. The usual advice? "Just migrate to Elasticsearch." But that always felt like using a sledgehammer to crack a nut.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Altus 4&lt;/strong&gt;, an AI-enhanced MySQL full-text search engine that works with your existing database infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not Just Use Elasticsearch?
&lt;/h2&gt;

&lt;p&gt;Don't get me wrong; Elasticsearch is incredible. But for most applications, it introduces unnecessary complexity:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Elasticsearch Route:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;New infrastructure&lt;/strong&gt; to provision, configure, and maintain&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data synchronization&lt;/strong&gt; nightmares between MySQL and ES&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learning curve&lt;/strong&gt; for your entire team&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operational overhead&lt;/strong&gt; of managing two data stores&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Additional costs&lt;/strong&gt; for hosting and maintenance&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Reality Check:
&lt;/h3&gt;

&lt;p&gt;Most applications don't need the full power of Elasticsearch. They just need MySQL to be smarter about search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Altus 4: Enhancing MySQL Instead of Replacing It
&lt;/h2&gt;

&lt;p&gt;The core insight behind Altus 4 is simple: &lt;strong&gt;What if we could make MySQL's existing full-text search intelligent without changing your data architecture?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's what that looks like in practice:&lt;/p&gt;

&lt;h3&gt;
  
  
  Before (Traditional MySQL Search):
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%premium coffee%'&lt;/span&gt; 
&lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%premium coffee%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  After (Altus 4 Enhanced):
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/api/v1/search &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "query": "find premium coffee products",
    "databases": ["ecommerce_db"],
    "searchMode": "natural"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference? Altus 4 understands that "premium coffee products" should also match "high-quality espresso beans" or "artisan coffee roasters" thanks to AI-powered semantic understanding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Architecture: How It Works
&lt;/h2&gt;

&lt;p&gt;Altus 4 sits between your application and MySQL databases, enhancing search capabilities through several layers:&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Components:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Database Service Layer&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connects to multiple MySQL databases&lt;/li&gt;
&lt;li&gt;Discovers existing full-text indexes&lt;/li&gt;
&lt;li&gt;Manages connection pooling and optimization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. AI Enhancement Layer&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI integration for semantic search&lt;/li&gt;
&lt;li&gt;Query optimization and natural language processing&lt;/li&gt;
&lt;li&gt;Relevance scoring improvements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Search Orchestration&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Federated search across multiple databases&lt;/li&gt;
&lt;li&gt;Result ranking and deduplication&lt;/li&gt;
&lt;li&gt;Real-time analytics and caching&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Magic: Semantic Search Implementation
&lt;/h3&gt;

&lt;p&gt;Here's a simplified version of how the semantic enhancement works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SearchService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;enhancedSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;databases&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Generate embeddings for the search query&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryEmbedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aiService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Execute traditional full-text search&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;traditionalResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executeFullTextSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;databases&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Enhance with semantic understanding&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;semanticResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findSemanticMatches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queryEmbedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;databases&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Combine and rank results&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rankAndCombineResults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;traditionalResults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;semanticResults&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;findSemanticMatches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;databases&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Find conceptually similar content even without exact keyword matches&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vectorSimilaritySearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;databases&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-World Use Case: E-commerce Search
&lt;/h2&gt;

&lt;p&gt;Let me show you a concrete example. I implemented Altus 4 for a client's e-commerce platform:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Challenge:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;50,000+ products across multiple categories&lt;/li&gt;
&lt;li&gt;Users searching with natural language ("gifts for coffee lovers")&lt;/li&gt;
&lt;li&gt;Existing MySQL setup they didn't want to change&lt;/li&gt;
&lt;li&gt;Need for real-time inventory integration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Implementation:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Setup (literally 5 minutes):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/altus4/core.git
&lt;span class="nb"&gt;cd &lt;/span&gt;altus4
npm &lt;span class="nb"&gt;install
cp&lt;/span&gt; .env.example .env
&lt;span class="c"&gt;# Configure MySQL and OpenAI credentials&lt;/span&gt;
npm run migrate
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Connect existing database:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/api/v1/databases &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "name": "E-commerce DB",
    "host": "localhost",
    "database": "ecommerce",
    "username": "db_user",
    "password": "password"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Start searching:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Natural language search&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v1/search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bearer YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gifts for coffee enthusiasts under $50&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;databases&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ecommerce_db&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;searchMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;natural&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&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="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Results:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Search relevance improved by 60%&lt;/strong&gt; (measured by click-through rates)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Setup time: 30 minutes&lt;/strong&gt; vs. weeks for Elasticsearch migration
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero downtime&lt;/strong&gt; during implementation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance overhead: minimal&lt;/strong&gt; (just keeping Altus 4 updated)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Comparison: Altus 4 vs. Traditional Approaches
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;MySQL LIKE&lt;/th&gt;
&lt;th&gt;Elasticsearch&lt;/th&gt;
&lt;th&gt;Altus 4&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup Time&lt;/td&gt;
&lt;td&gt;5 minutes&lt;/td&gt;
&lt;td&gt;2-4 weeks&lt;/td&gt;
&lt;td&gt;30 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure Changes&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Significant&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Semantic Search&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Natural Language&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operational Complexity&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-Database Search&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Complex&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost (monthly)&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;$500-2000+&lt;/td&gt;
&lt;td&gt;$50-200&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Getting Started: Your First Search in 10 Minutes
&lt;/h2&gt;

&lt;p&gt;Want to try this out? Here's a complete walkthrough:&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 18+&lt;/li&gt;
&lt;li&gt;MySQL 8.0+
&lt;/li&gt;
&lt;li&gt;Redis 6.0+&lt;/li&gt;
&lt;li&gt;OpenAI API key (optional for basic features)&lt;/li&gt;
&lt;/ul&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/altus4/core.git
&lt;span class="nb"&gt;cd &lt;/span&gt;altus4
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Environment Setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;.env&lt;/code&gt; with your credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DB_HOST=localhost
DB_USERNAME=your_mysql_user
DB_PASSWORD=your_mysql_password
DB_DATABASE=altus4_metadata

REDIS_HOST=localhost
REDIS_PORT=6379

OPENAI_API_KEY=sk-your-openai-key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Database Setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run migrations to create Altus 4 metadata tables&lt;/span&gt;
npm run migrate

&lt;span class="c"&gt;# Start the server&lt;/span&gt;
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Add Your First Database
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/api/v1/auth/register &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "email": "test@example.com",
    "password": "secure_password",
    "name": "Test User"
  }'&lt;/span&gt;

&lt;span class="c"&gt;# Follow the authentication flow to get your API key&lt;/span&gt;
&lt;span class="c"&gt;# Then add your database connection&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Your First Search
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v1/search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bearer YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user accounts created last month&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;databases&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your_db_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;searchMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;natural&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;searchResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When Should You Use Altus 4?
&lt;/h2&gt;

&lt;p&gt;Altus 4 is perfect for:&lt;/p&gt;

&lt;h3&gt;
  
  
  Great Fit:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Existing MySQL applications&lt;/strong&gt; needing better search&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Teams wanting to avoid Elasticsearch complexity&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SaaS applications&lt;/strong&gt; with user-generated content&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;E-commerce platforms&lt;/strong&gt; with product catalogs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal tools&lt;/strong&gt; and admin dashboards&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prototypes and MVPs&lt;/strong&gt; needing professional search quickly&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Consider Alternatives:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Massive scale&lt;/strong&gt; (millions of searches per second)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex aggregation requirements&lt;/strong&gt; beyond search&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teams already invested&lt;/strong&gt; in Elasticsearch ecosystem&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Applications requiring&lt;/strong&gt; millisecond response times at scale&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Technical Deep-Dive: Architecture Decisions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why TypeScript?
&lt;/h3&gt;

&lt;p&gt;Type safety was crucial for this project. With multiple database connections, AI integrations, and complex search logic, TypeScript caught countless potential runtime errors during development.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Express.js?
&lt;/h3&gt;

&lt;p&gt;Familiarity and ecosystem. Most developers know Express, and the middleware ecosystem is unmatched for authentication, validation, and logging.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why MySQL for Metadata?
&lt;/h3&gt;

&lt;p&gt;Dogfooding our own solution. Altus 4's own metadata (user accounts, API keys, database connections) is stored in MySQL and searchable through the same API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Redis?
&lt;/h3&gt;

&lt;p&gt;Two main use cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Caching search results&lt;/strong&gt; for performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storing search analytics&lt;/strong&gt; and user behavior data&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Challenges and Lessons Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Challenge 1: Connection Pool Management
&lt;/h3&gt;

&lt;p&gt;Managing connections to multiple MySQL databases efficiently required custom pooling logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DatabaseConnectionManager&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;pools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Pool&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;databaseId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PoolConnection&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;databaseId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDatabaseConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;databaseId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;databaseId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createPool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;databaseId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getConnection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Challenge 2: AI Rate Limiting
&lt;/h3&gt;

&lt;p&gt;OpenAI has rate limits. For semantic search, we implemented intelligent batching:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AIService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;requestQueue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;QueryQueue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;QueryQueue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;generateEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Batch multiple requests and respect rate limits&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createEmbedding&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-embedding-ada-002&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Challenge 3: Result Ranking
&lt;/h3&gt;

&lt;p&gt;Combining traditional full-text scores with semantic similarity scores required experimentation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;calculateCombinedScore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;fullTextScore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;semanticScore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;boost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Weighted combination with configurable boost&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fullTextScore&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;semanticScore&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;boost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's Next: Roadmap
&lt;/h2&gt;

&lt;p&gt;I'm actively developing Altus 4 with these priorities:&lt;/p&gt;

&lt;h3&gt;
  
  
  Short-term (Next 3 months):
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vector database integration&lt;/strong&gt; for better semantic search performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GraphQL API&lt;/strong&gt; alongside REST&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhook support&lt;/strong&gt; for real-time search index updates&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Advanced analytics dashboard&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Long-term (6+ months):
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Auto-scaling capabilities&lt;/strong&gt; for high-traffic applications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Machine learning&lt;/strong&gt; for personalized search ranking
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-language support&lt;/strong&gt; beyond English&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plugin ecosystem&lt;/strong&gt; for custom search behaviors&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Open Source and Community
&lt;/h2&gt;

&lt;p&gt;Altus 4 is completely open source (Apache 2.0 License). I built this to solve a real problem I kept encountering, and I'm excited to see how the community uses and improves it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Contributing:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: [Repository link]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issues&lt;/strong&gt;: Bug reports and feature requests welcome&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pull Requests&lt;/strong&gt;: All skill levels welcome&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt;: Always needs improvement&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Community:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Discord&lt;/strong&gt;: Join our developer community&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Examples&lt;/strong&gt;: Real-world implementation examples&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blog&lt;/strong&gt;: Regular technical deep-dives&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion: Solving Real Problems
&lt;/h2&gt;

&lt;p&gt;Building Altus 4 taught me that sometimes the best solution isn't the most obvious one. Instead of joining the "just use Elasticsearch" chorus, I asked: "What if we made MySQL smarter instead?"&lt;/p&gt;

&lt;p&gt;The result is a tool that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solves the search problem&lt;/strong&gt; for 80% of applications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Respects existing architecture&lt;/strong&gt; decisions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduces operational complexity&lt;/strong&gt; instead of adding to it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provides enterprise features&lt;/strong&gt; without enterprise overhead&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're struggling with search in your MySQL-based application, give Altus 4 a try. It might just be the "good enough" solution that's actually better than the complicated alternative.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try Altus 4:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://altus4.thavarshan.com" rel="noopener noreferrer"&gt;https://altus4.thavarshan.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/altus4/core" rel="noopener noreferrer"&gt;https://github.com/altus4/core&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt;: &lt;a href="https://altus4.thavarshan.com/docs" rel="noopener noreferrer"&gt;https://altus4.thavarshan.com/docs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; #mysql #opensourcedev #ai #searchengine #typescript #nodejs #elasticsearch&lt;/p&gt;

</description>
      <category>mysql</category>
      <category>ai</category>
      <category>typescript</category>
      <category>searchengine</category>
    </item>
    <item>
      <title>Wisp a Modern, Secure One-Time Secret Sharing App Built with Laravel 12, Vue 3 and Inertia.</title>
      <dc:creator>Jerome Thayananthajothy</dc:creator>
      <pubDate>Fri, 15 Aug 2025 01:13:07 +0000</pubDate>
      <link>https://dev.to/thavarshan/wisp-a-modern-secure-one-time-secret-sharing-app-built-with-laravel-12-vue-3-and-inertiajs-39e4</link>
      <guid>https://dev.to/thavarshan/wisp-a-modern-secure-one-time-secret-sharing-app-built-with-laravel-12-vue-3-and-inertiajs-39e4</guid>
      <description>&lt;p&gt;In a world where sensitive data leaks happen daily, sharing passwords, API keys, or confidential information over email or chat is risky.&lt;br&gt;
We’ve all been there — “Can you send me the database password?” and before you know it, it’s stored permanently in Slack history or someone’s inbox.&lt;/p&gt;

&lt;p&gt;That’s the problem &lt;strong&gt;Wisp&lt;/strong&gt; solves.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Solution: Wisp
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://wisp.thavarshan.com" rel="noopener noreferrer"&gt;Wisp&lt;/a&gt;&lt;/strong&gt; is a secure, one-time secret sharing application.&lt;br&gt;
It allows you to send encrypted secrets via unique links that expire after being viewed or after a set period of time, after which they are permanently deleted.&lt;/p&gt;

&lt;p&gt;Key highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secrets are encrypted using AES-256-CBC before storage&lt;/li&gt;
&lt;li&gt;Optional password protection&lt;/li&gt;
&lt;li&gt;Links auto-expire after minutes, hours, or days&lt;/li&gt;
&lt;li&gt;Secrets are deleted permanently after first viewing&lt;/li&gt;
&lt;li&gt;No decrypted secret is ever stored server-side&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpx1g10uu0qgf2hlhtnut.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpx1g10uu0qgf2hlhtnut.png" alt="Wisp New Secret Form" width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Technical Deep-Dive
&lt;/h2&gt;

&lt;p&gt;Wisp is built with a stack that prioritizes security, developer productivity, and a modern user experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Laravel 12.21 with PHP 8.4&lt;/li&gt;
&lt;li&gt;Laravel’s AES-256-CBC encryption&lt;/li&gt;
&lt;li&gt;Bcrypt password hashing&lt;/li&gt;
&lt;li&gt;MySQL 8.0+&lt;/li&gt;
&lt;li&gt;PHPUnit for backend tests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Frontend&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vue 3.5 (Composition API) with TypeScript&lt;/li&gt;
&lt;li&gt;Inertia.js 2.0 for seamless Laravel-Vue integration&lt;/li&gt;
&lt;li&gt;Tailwind CSS 3.4 with &lt;a href="https://ui.shadcn.com/" rel="noopener noreferrer"&gt;shadcn/ui&lt;/a&gt; for consistent UI components&lt;/li&gt;
&lt;li&gt;Ziggy for Laravel routes in JavaScript&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Hosting&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deployed on Heroku with HTTPS enforced&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Security Implementation
&lt;/h2&gt;

&lt;p&gt;Wisp’s core principle: encrypt sensitive data immediately, destroy it quickly, and never store it in plain text.&lt;/p&gt;

&lt;p&gt;How it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Secrets are encrypted with AES-256-CBC before saving to the database.&lt;/li&gt;
&lt;li&gt;If a password is set, it is hashed using Bcrypt and verified before decryption.&lt;/li&gt;
&lt;li&gt;On first view, the secret is permanently deleted.&lt;/li&gt;
&lt;li&gt;If not viewed, it is automatically removed at the set expiration time.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Laravel encryption example&lt;/span&gt;
&lt;span class="nv"&gt;$encrypted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$secretContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$decrypted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;decrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$encrypted&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fakqlcdknli8wjlrymlp0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fakqlcdknli8wjlrymlp0.png" alt="Wisp Password Protected View" width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Inertia.js?
&lt;/h2&gt;

&lt;p&gt;Instead of building a traditional API with a separate frontend, Wisp uses Inertia.js to connect Laravel and Vue directly.&lt;/p&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No need for API controllers or serialization layers&lt;/li&gt;
&lt;li&gt;Laravel handles routing and authentication natively&lt;/li&gt;
&lt;li&gt;SPA experience without managing a separate API stack&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example Inertia route in Laravel:&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="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/secrets/create'&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Inertia&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Secrets/Create'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'secrets.create'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13778mj0kpp44nwqvkrb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13778mj0kpp44nwqvkrb.png" alt="Wisp Secret View with Deletion Notice" width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Developer Experience
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Laravel Pint for automatic code formatting&lt;/li&gt;
&lt;li&gt;Nightwatch.js for end-to-end testing of the secret sharing flow&lt;/li&gt;
&lt;li&gt;Full TypeScript support for frontend predictability&lt;/li&gt;
&lt;li&gt;shadcn/ui for accessible and reusable components&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Clone the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Thavarshan/wisp.git
&lt;span class="nb"&gt;cd &lt;/span&gt;wisp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer &lt;span class="nb"&gt;install
&lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
php artisan key:generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run migrations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan serve
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Visit &lt;code&gt;/&lt;/code&gt; and create a new secret&lt;/li&gt;
&lt;li&gt;Set an optional password and expiration time&lt;/li&gt;
&lt;li&gt;Share the generated link&lt;/li&gt;
&lt;li&gt;Once opened, the secret is permanently deleted&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13778mj0kpp44nwqvkrb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13778mj0kpp44nwqvkrb.png" alt="Wisp Secret View with Deletion Notice" width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Contributing
&lt;/h2&gt;

&lt;p&gt;Wisp is open-source and welcomes contributions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/Thavarshan/wisp" rel="noopener noreferrer"&gt;https://github.com/Thavarshan/wisp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Issues and pull requests are encouraged&lt;/li&gt;
&lt;li&gt;Feedback is always welcome&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Call to Action
&lt;/h2&gt;

&lt;p&gt;If you’ve ever sent a password over chat and worried about where it might end up, Wisp is for you.&lt;br&gt;
Try it out, give it a star on GitHub, and help improve it. Together, we can make secure sharing the default.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>laravel</category>
      <category>vue</category>
    </item>
    <item>
      <title>Introducing Filterable: A Powerful, Modular Query Filtering System for Laravel</title>
      <dc:creator>Jerome Thayananthajothy</dc:creator>
      <pubDate>Tue, 13 May 2025 19:18:51 +0000</pubDate>
      <link>https://dev.to/thavarshan/introducing-filterable-a-powerful-modular-query-filtering-system-for-laravel-2gnl</link>
      <guid>https://dev.to/thavarshan/introducing-filterable-a-powerful-modular-query-filtering-system-for-laravel-2gnl</guid>
      <description>&lt;p&gt;If you've built Laravel applications that require complex filtering of your Eloquent models, you've likely faced the challenge of keeping your controller code clean while implementing robust query filters. Today, I'm excited to introduce &lt;strong&gt;Filterable&lt;/strong&gt;, a new open-source package that makes dynamic query filtering both powerful and simple with its modular, trait-based architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Messy Controller Code
&lt;/h2&gt;

&lt;p&gt;We've all been there. Your controller starts simple, but as filtering requirements grow, it quickly becomes cluttered:&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;index&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;$query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;query&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;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;has&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="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&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="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&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;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'category'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&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="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;);&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;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'search'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'like'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%&lt;/span&gt;&lt;span class="si"&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="n"&gt;search&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// More conditional filters...&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paginate&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;As your application grows, this approach leads to bloated controllers, duplicated code, and difficult-to-maintain filter logic. That's where Filterable comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Filterable
&lt;/h2&gt;

&lt;p&gt;Filterable provides a clean, modular approach to filter your Laravel Eloquent queries based on request parameters. But it doesn't stop there - it offers a rich set of features through its trait-based architecture, allowing you to pick and choose functionality based on your needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Filtering&lt;/strong&gt;: Apply filters based on request parameters with ease&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modular Architecture&lt;/strong&gt;: Customize your filter implementation using traits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart Caching&lt;/strong&gt;: Intelligent caching with automatic cache key generation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User-Specific Filtering&lt;/strong&gt;: Easily implement user-scoped filters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate Limiting&lt;/strong&gt;: Control filter complexity to prevent abuse&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Monitoring&lt;/strong&gt;: Track execution time and query performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Management&lt;/strong&gt;: Optimize memory usage for large datasets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query Optimization&lt;/strong&gt;: Select specific columns and eager-load relationships&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filter Chaining&lt;/strong&gt;: Chain multiple filter operations with a fluent API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Laravel 12 Support&lt;/strong&gt;: Ready for the latest Laravel version&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Installation is simple with Composer:&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 jerome/filterable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating Your First Filter
&lt;/h3&gt;

&lt;p&gt;Create a filter class using the provided Artisan command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan make:filter PostFilter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates a well-structured filter class:&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;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Filters&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;Filterable\Filter&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\Database\Eloquent\Builder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostFilter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Filter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$filters&lt;/span&gt; &lt;span class="o"&gt;=&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;'category'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Builder&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&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="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Builder&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Implementing in Your Model
&lt;/h3&gt;

&lt;p&gt;Add the trait to your model:&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;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Models&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;Filterable\Traits\Filterable&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\Database\Eloquent\Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Model&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;Filterable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using in Your Controller
&lt;/h3&gt;

&lt;p&gt;Your controller code becomes clean and maintainable:&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;index&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="kt"&gt;PostFilter&lt;/span&gt; &lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$filter&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;paginate&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;response&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;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$posts&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's it! Your API now supports filters like &lt;code&gt;/posts?status=published&amp;amp;category=5&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Makes Filterable Different?
&lt;/h2&gt;

&lt;p&gt;Filterable stands out from other filtering packages due to its modular, trait-based architecture. Let's explore some of its most powerful features.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature Management
&lt;/h3&gt;

&lt;p&gt;Enable only the features you need:&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;$filter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;enableFeatures&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'validation'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'caching'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'performance'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rate Limiting and Complexity Control
&lt;/h3&gt;

&lt;p&gt;Protect your application from overly complex filter requests:&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;$filter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setMaxFilters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setMaxComplexity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setFilterComplexity&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'complex_filter'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'simple_filter'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Memory Management for Large Datasets
&lt;/h3&gt;

&lt;p&gt;Handle large datasets efficiently:&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="c1"&gt;// Process with lazy loading&lt;/span&gt;
&lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;lazy&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;each&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="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Process each post with minimal memory&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Map over results without loading all records&lt;/span&gt;
&lt;span class="nv"&gt;$titles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;map&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="nv"&gt;$post&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="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Smart Caching
&lt;/h3&gt;

&lt;p&gt;Improve performance with intelligent caching:&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;$filter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCacheExpiration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cacheTags&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'posts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'api'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cacheResults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Input Validation
&lt;/h3&gt;

&lt;p&gt;Validate filter parameters before applying them:&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;$filter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setValidationRules&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'status'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|in:draft,published,archived'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'category_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'sometimes|integer|exists:categories,id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Performance Monitoring
&lt;/h3&gt;

&lt;p&gt;Track execution time and query performance:&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;$metrics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMetrics&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$executionTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getExecutionTime&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-World Example
&lt;/h2&gt;

&lt;p&gt;Let's see how Filterable brings it all together in a real-world controller:&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;index&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="kt"&gt;PostFilter&lt;/span&gt; &lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Enable features&lt;/span&gt;
    &lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;enableFeatures&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'validation'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'caching'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'performance'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;// Set validation rules&lt;/span&gt;
    &lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setValidationRules&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'status'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'sometimes|in:draft,published,archived'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'category_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'sometimes|integer|exists:categories,id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;// Apply user scope if needed&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;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'my_posts'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;forUser&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;user&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Apply pre-filters&lt;/span&gt;
    &lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;registerPreFilters&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="nv"&gt;$query&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="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'is_deleted'&lt;/span&gt;&lt;span class="p"&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="c1"&gt;// Apply custom filter chain&lt;/span&gt;
    &lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'is_featured'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&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;orderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'created_at'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'desc'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Apply filters to the query&lt;/span&gt;
    &lt;span class="nv"&gt;$query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Get paginated results&lt;/span&gt;
    &lt;span class="nv"&gt;$posts&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;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'paginate'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paginate&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;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'per_page'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Return performance metrics for debugging&lt;/span&gt;
    &lt;span class="nv"&gt;$metrics&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;app&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;environment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'local'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'performance'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$metrics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMetrics&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;response&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;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'data'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'metrics'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$metrics&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With just a few lines of code, you've implemented a sophisticated filtering system with validation, caching, user-scoping, and performance monitoring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extending Filterable
&lt;/h2&gt;

&lt;p&gt;One of Filterable's greatest strengths is its extensibility. Need a custom filter behavior? Simply extend the base filter class:&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostFilter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Filter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Override or extend methods as needed&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handleFilteringException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&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="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'filter-errors'&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Filter error'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'exception'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="c1"&gt;// Custom handling logic&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Laravel 12 Support
&lt;/h2&gt;

&lt;p&gt;Filterable is fully compatible with Laravel 12's new application structure. If you're using Laravel 12 with its minimal setup, you can register the service provider in your &lt;code&gt;bootstrap/providers.php&lt;/code&gt; file:&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// Other providers...&lt;/span&gt;
    &lt;span class="nc"&gt;Filterable\Providers\FilterableServiceProvider&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Filterable offers a clean, modular approach to handling query filters in Laravel applications. With its trait-based architecture, you get exactly the features you need without unnecessary overhead. Whether you're building a simple blog or a complex data platform, Filterable helps you keep your code clean and maintainable while offering powerful filtering capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started Today
&lt;/h2&gt;

&lt;p&gt;Ready to try Filterable in your Laravel application? Check out the &lt;a href="https://github.com/Thavarshan/filterable" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; and give it a star if you find it useful!&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 jerome/filterable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We welcome contributions and feedback as we continue to improve the package. Let us know what features you'd like to see next!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>php</category>
      <category>laravel</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building PHPVM: A PHP Version Manager Born from Necessity</title>
      <dc:creator>Jerome Thayananthajothy</dc:creator>
      <pubDate>Mon, 12 May 2025 10:26:47 +0000</pubDate>
      <link>https://dev.to/thavarshan/building-phpvm-a-php-version-manager-born-from-necessity-25fn</link>
      <guid>https://dev.to/thavarshan/building-phpvm-a-php-version-manager-born-from-necessity-25fn</guid>
      <description>&lt;p&gt;As a PHP developer working across multiple projects, I found myself constantly struggling with a common problem: managing different PHP versions. Each client, each project had its own requirements. One legacy application needed PHP 7.4 for compatibility reasons, while a modern API required PHP 8.2 to leverage newer language features.&lt;/p&gt;

&lt;p&gt;Sound familiar? If you're nodding your head, you're not alone. This is why I created &lt;a href="https://github.com/Thavarshan/phpvm" rel="noopener noreferrer"&gt;&lt;code&gt;phpvm&lt;/code&gt;&lt;/a&gt;, a lightweight PHP version manager inspired by the elegant simplicity of Node Version Manager (NVM).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: PHP Version Chaos
&lt;/h2&gt;

&lt;p&gt;Like many developers, I work with multiple PHP applications simultaneously. Before creating &lt;code&gt;phpvm&lt;/code&gt;, my workflow looked something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check project requirements&lt;/li&gt;
&lt;li&gt;Manually uninstall current PHP version&lt;/li&gt;
&lt;li&gt;Install required PHP version&lt;/li&gt;
&lt;li&gt;Configure system to use it&lt;/li&gt;
&lt;li&gt;Repeat steps 1-4 every time I switched projects&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This process was not only time-consuming but error-prone. Sometimes I'd miss a step, resulting in cryptic errors and wasted debugging time. Other times, I'd accidentally break system dependencies that relied on specific PHP versions.&lt;/p&gt;

&lt;p&gt;"There has to be a better way," I thought. Looking around, I found several solutions, but none had the simplicity and elegance I was seeking. As a TypeScript and PHP developer who uses tools like NVM daily, I wanted something that followed similar patterns and conventions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Journey to Create phpvm
&lt;/h2&gt;

&lt;p&gt;Building a version manager that works across different operating systems presented numerous challenges. What works on macOS might fail on Ubuntu, and what functions on Arch Linux might break on CentOS.&lt;/p&gt;

&lt;p&gt;The first hurdle was detecting the system's package manager. Mac users typically use Homebrew, while Linux distributions vary between apt, dnf, yum, and pacman. Each has its own syntax and peculiarities.&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;# A snippet showing how phpvm detects the system's package manager&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Darwin"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;PKG_MANAGER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"brew"&lt;/span&gt;
    &lt;span class="c"&gt;# macOS-specific logic&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="c"&gt;# Detect Linux package manager&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; apt-get &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nv"&gt;PKG_MANAGER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"apt"&lt;/span&gt;
    &lt;span class="k"&gt;elif &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; dnf &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nv"&gt;PKG_MANAGER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"dnf"&lt;/span&gt;
    &lt;span class="c"&gt;# More package managers...&lt;/span&gt;
    &lt;span class="k"&gt;fi
fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another significant challenge came with managing symlinks and paths. On macOS, Homebrew installs PHP in a Cellar directory, while Linux distributions typically use &lt;code&gt;/usr/bin&lt;/code&gt;. Handling these differences while providing a consistent user experience required careful consideration.&lt;/p&gt;

&lt;p&gt;Permissions posed yet another obstacle. Installing PHP often requires superuser privileges, but running commands as root can introduce security risks. I implemented a &lt;code&gt;run_with_sudo&lt;/code&gt; helper function that elevates privileges only when necessary.&lt;/p&gt;

&lt;p&gt;Testing across multiple environments was perhaps the most time-consuming part. I needed to ensure phpvm worked correctly not just on my machine but on various Linux distributions and macOS versions. This led me to create a comprehensive self-testing system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phpvm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command runs a series of tests that verify core functionality, giving users confidence that everything is working as expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: phpvm in Action
&lt;/h2&gt;

&lt;p&gt;After several iterations, &lt;code&gt;phpvm&lt;/code&gt; emerged as a robust solution. Here's how it transforms the PHP version management workflow:&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;# Install a specific PHP version&lt;/span&gt;
phpvm &lt;span class="nb"&gt;install &lt;/span&gt;8.1

&lt;span class="c"&gt;# Switch to that version&lt;/span&gt;
phpvm use 8.1

&lt;span class="c"&gt;# Check the active version&lt;/span&gt;
php &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;span class="c"&gt;# Output: PHP 8.1.13&lt;/span&gt;

&lt;span class="c"&gt;# Switch to another version&lt;/span&gt;
phpvm use 7.4

&lt;span class="c"&gt;# Verify the change&lt;/span&gt;
php &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;span class="c"&gt;# Output: PHP 7.4.33&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For teams working on the same project, phpvm supports automatic version switching via a &lt;code&gt;.phpvmrc&lt;/code&gt; file:&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;# Create a .phpvmrc file in your project root&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"8.2"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .phpvmrc

&lt;span class="c"&gt;# Later, when entering the project directory&lt;/span&gt;
phpvm auto
&lt;span class="c"&gt;# Output: Auto-switching to PHP 8.2 (from /path/to/project/.phpvmrc)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This feature ensures all team members use the correct PHP version, eliminating the "works on my machine" syndrome.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Design Decisions
&lt;/h2&gt;

&lt;p&gt;Several design principles guided phpvm's development:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Shell Compatibility&lt;/strong&gt;: Written in POSIX-compliant shell script to ensure maximum compatibility across systems.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Minimal Dependencies&lt;/strong&gt;: phpvm relies only on standard Unix tools and the system's package manager, avoiding additional dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Informative Feedback&lt;/strong&gt;: Every operation provides clear, timestamped output, making it easier to understand what's happening:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2025-05-12 10:15:30 [INFO] Switching to PHP 8.1...
2025-05-12 10:15:32 [INFO] Switched to PHP 8.1.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Robust Error Handling&lt;/strong&gt;: When something goes wrong, phpvm provides detailed error messages and suggestions, rather than failing silently.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automated Testing&lt;/strong&gt;: The built-in testing framework ensures reliability across different environments.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These decisions contribute to a tool that's not only functional but also maintainable and user-friendly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking Ahead: The Future of phpvm
&lt;/h2&gt;

&lt;p&gt;While phpvm successfully solves the PHP version management problem, I see several exciting possibilities for its future:&lt;/p&gt;

&lt;h2&gt;
  
  
  Try phpvm Today
&lt;/h2&gt;

&lt;p&gt;If you're tired of the PHP version management dance, I invite you to try phpvm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-o-&lt;/span&gt; https://raw.githubusercontent.com/Thavarshan/phpvm/main/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project is open source and available at &lt;a href="https://github.com/Thavarshan/phpvm" rel="noopener noreferrer"&gt;github.com/Thavarshan/phpvm&lt;/a&gt;. I welcome your feedback, bug reports, and contributions! Whether you're using phpvm daily or just trying it out, your experience helps shape its future.&lt;/p&gt;

&lt;p&gt;The journey of creating phpvm reminded me that great developer tools often arise from personal frustrations. By solving my own PHP version management headaches, I hope to help others streamline their workflows and focus on what truly matters—building great PHP applications.&lt;/p&gt;

&lt;p&gt;What developer tools have you created to solve your own problems? I'd love to hear about your experiences in the comments below.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>bash</category>
      <category>php</category>
      <category>shell</category>
    </item>
    <item>
      <title>Bringing JavaScript’s fetch() to PHP (Sort Of)</title>
      <dc:creator>Jerome Thayananthajothy</dc:creator>
      <pubDate>Mon, 12 May 2025 10:05:34 +0000</pubDate>
      <link>https://dev.to/thavarshan/bringing-javascripts-fetch-to-php-sort-of-1bi6</link>
      <guid>https://dev.to/thavarshan/bringing-javascripts-fetch-to-php-sort-of-1bi6</guid>
      <description>&lt;p&gt;If you’ve worked across both frontend and backend stacks, you’ve probably felt the contrast.&lt;/p&gt;

&lt;p&gt;On the frontend, JavaScript offers an elegant experience. Making an HTTP request is as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s expressive, minimal, and easy to reason about.&lt;/p&gt;

&lt;p&gt;But then you switch over to PHP, and the flow changes. You reach for a well-established library like Guzzle or Symfony’s HTTP client, and suddenly things feel a bit heavier. There’s nothing wrong with them — in fact, they’re excellent. But the ergonomics are just... different.&lt;/p&gt;

&lt;p&gt;That friction sparked a small idea.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What if PHP had something that felt like JavaScript’s &lt;code&gt;fetch()&lt;/code&gt;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A Familiar Syntax in a Different Language
&lt;/h2&gt;

&lt;p&gt;That question led to the creation of &lt;a href="https://github.com/Thavarshan/fetch-php" rel="noopener noreferrer"&gt;Fetch PHP&lt;/a&gt;, an HTTP client library designed to bring a more fluent and familiar interface to PHP.&lt;/p&gt;

&lt;p&gt;It started as a personal experiment. The goal wasn’t to build a better HTTP client than existing ones — just one that felt more intuitive to use if you were already accustomed to JavaScript's conventions.&lt;/p&gt;

&lt;p&gt;Here’s what that looks like in practice:&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;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://api.example.com/data'&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;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, if you prefer working asynchronously with promises:&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;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;await&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://api.example.com/data'&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;async&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;withHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Authorization'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Bearer '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$token&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;withTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&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;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What It Offers
&lt;/h2&gt;

&lt;p&gt;Fetch PHP isn’t a rewrite of how HTTP works in PHP — it’s an abstraction over existing standards and tools, with a focus on developer experience.&lt;/p&gt;

&lt;p&gt;Some of the features include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fluent, chainable syntax&lt;/strong&gt; for building requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Support for synchronous and asynchronous workflows&lt;/strong&gt;, using ReactPHP-style promises&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic retries&lt;/strong&gt; with exponential backoff and error handling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PSR-7 and PSR-18 compatibility&lt;/strong&gt; for interoperability with other libraries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PSR-3 logging support&lt;/strong&gt;, including masking of sensitive data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immutable request handling&lt;/strong&gt;, reducing unintended side effects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is to keep things expressive and minimal, while still offering the power and flexibility needed for real-world use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons from Building It
&lt;/h2&gt;

&lt;p&gt;The process of building Fetch PHP was a reminder that language differences don’t have to limit developer experience. PHP may not be inherently asynchronous, but with the right tools, it can be. And even if it’s not JavaScript, it can still feel approachable and expressive.&lt;/p&gt;

&lt;p&gt;It’s easy to underestimate the impact of ergonomics. Developer comfort matters — and sometimes, reaching for a more intuitive syntax can make a meaningful difference in how you approach and enjoy your work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give It a Try
&lt;/h2&gt;

&lt;p&gt;If you’ve ever found yourself wishing PHP had a more streamlined way to work with HTTP, Fetch PHP might be worth a look.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Thavarshan/fetch-php" rel="noopener noreferrer"&gt;Fetch PHP on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fetch-php.thavarshan.com/guide/quickstart" rel="noopener noreferrer"&gt;Quickstart Guide and Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feedback and contributions are always welcome.&lt;/p&gt;

</description>
      <category>php</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Introducing Fetch PHP 3.0: JavaScript-like HTTP Requests for Modern PHP Applications</title>
      <dc:creator>Jerome Thayananthajothy</dc:creator>
      <pubDate>Sun, 04 May 2025 10:36:07 +0000</pubDate>
      <link>https://dev.to/thavarshan/introducing-fetch-php-30-javascript-like-http-requests-for-modern-php-applications-209i</link>
      <guid>https://dev.to/thavarshan/introducing-fetch-php-30-javascript-like-http-requests-for-modern-php-applications-209i</guid>
      <description>&lt;p&gt;We're excited to announce the release of Fetch PHP 3.0, a major update to our HTTP client library that brings the intuitive experience of JavaScript's &lt;code&gt;fetch()&lt;/code&gt; API to PHP developers. This release represents a significant advancement in how PHP applications can handle HTTP requests, with powerful new features for both synchronous and asynchronous operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Vision Behind Fetch PHP
&lt;/h2&gt;

&lt;p&gt;When we first created Fetch PHP, our goal was simple: to bring the elegance and simplicity of JavaScript's fetch API to the PHP ecosystem. Frontend developers have long enjoyed the intuitive nature of &lt;code&gt;fetch()&lt;/code&gt; for making HTTP requests, and we wanted backend developers to have the same experience.&lt;/p&gt;

&lt;p&gt;With version 3.0, we've taken this vision even further by enhancing the library with more powerful features while maintaining the clean, intuitive API that made it popular.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's New in Version 3.0
&lt;/h2&gt;

&lt;h3&gt;
  
  
  True Asynchronous Support
&lt;/h3&gt;

&lt;p&gt;Fetch PHP 3.0 introduces full asynchronous request capabilities powered by our custom Matrix async engine. This allows PHP developers to write non-blocking code with JavaScript-like syntax:&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="c1"&gt;// Provided by Matrix PHP (https://github.com/Thavarshan/matrix)&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;async&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Promise-based approach&lt;/span&gt;
&lt;span class="nf"&gt;async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://api.example.com/users'&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;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$error&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;handleError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$error&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Or using async/await pattern&lt;/span&gt;
&lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;await&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://api.example.com/users'&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This async capability is game-changing for PHP applications that need to perform multiple concurrent operations, such as fetching data from several APIs or services simultaneously.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enhanced Fluent API
&lt;/h3&gt;

&lt;p&gt;The fluent API has been significantly improved in version 3.0, offering more flexibility and cleaner syntax:&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;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch&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;baseUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://api.example.com'&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;withHeaders&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'Accept'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'application/json'&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;withToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'your-access-token'&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;withJson&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'John Doe'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'john@example.com'&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;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/users'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This chainable approach makes building complex requests intuitive and readable, reducing the cognitive load when working with HTTP requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advanced Concurrency Controls
&lt;/h3&gt;

&lt;p&gt;Version 3.0 introduces robust tools for managing concurrent requests, inspired by JavaScript's Promise 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="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;async&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Run multiple requests in parallel&lt;/span&gt;
&lt;span class="nv"&gt;$results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;await&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'users'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://api.example.com/users'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="s1"&gt;'posts'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://api.example.com/posts'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="s1"&gt;'comments'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://api.example.com/comments'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;]));&lt;/span&gt;

&lt;span class="c1"&gt;// Access results&lt;/span&gt;
&lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'users'&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;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'posts'&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;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern is ideal for dashboard applications, aggregation services, or any scenario where you need to collect data from multiple sources efficiently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Intelligent Retry Mechanism
&lt;/h3&gt;

&lt;p&gt;Fetch PHP 3.0 includes a sophisticated retry system with exponential backoff:&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;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch&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;retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// 3 retries with 100ms initial delay&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://api.example.com/unstable-endpoint'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The library automatically retries failed requests with increasing delays between attempts, making your applications more resilient against transient network issues or service outages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comprehensive Response Object
&lt;/h3&gt;

&lt;p&gt;The Response class has been enhanced with more methods for working with different content types:&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;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://api.example.com/users/1'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// JSON handling&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;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Status checking&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;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isOk&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Success&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;elseif&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isNotFound&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Handle 404&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Header access&lt;/span&gt;
&lt;span class="nv"&gt;$contentType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response object now implements ArrayAccess, allowing you to use array syntax to access JSON response data directly:&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;$userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$userName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance Improvements
&lt;/h2&gt;

&lt;p&gt;Performance has been a key focus for version 3.0. We've optimized the core request handling to reduce overhead, resulting in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster response times for synchronous requests&lt;/li&gt;
&lt;li&gt;Lower memory usage during concurrent operations&lt;/li&gt;
&lt;li&gt;Better handling of large response bodies&lt;/li&gt;
&lt;li&gt;More efficient connection pooling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These improvements make Fetch PHP 3.0 suitable for high-performance applications that need to process many requests quickly and efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upgrading from Previous Versions
&lt;/h2&gt;

&lt;p&gt;For users of Fetch PHP 2.x, upgrading to version 3.0 is straightforward in most cases. We've maintained backwards compatibility with the core API while adding new features. A few breaking changes were necessary to support the new async capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Ready to try Fetch PHP 3.0? Installation is simple with Composer:&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 jerome/fetch-php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our &lt;a href="https://fetch-php.thavarshan.com" rel="noopener noreferrer"&gt;comprehensive documentation&lt;/a&gt; includes a quick start guide, detailed API reference, and numerous examples to help you get up and running quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future of Fetch PHP
&lt;/h2&gt;

&lt;p&gt;With version 3.0, Fetch PHP has matured into a robust, feature-rich HTTP client that bridges the gap between frontend and backend development patterns. We're committed to continuing this evolution with future enhancements focused on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expanded middleware support&lt;/li&gt;
&lt;li&gt;Additional content type handlers&lt;/li&gt;
&lt;li&gt;More advanced caching mechanisms&lt;/li&gt;
&lt;li&gt;Enhanced debugging tools&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Fetch PHP 3.0 represents a significant step forward in how PHP developers can interact with HTTP resources. By combining the familiar, intuitive syntax of JavaScript's fetch API with the power and flexibility of PHP, we've created a library that makes complex HTTP operations simpler and more maintainable.&lt;/p&gt;

&lt;p&gt;Whether you're building APIs, integrating with third-party services, or developing microservices, Fetch PHP 3.0 provides the tools you need to handle HTTP requests elegantly and efficiently.&lt;/p&gt;

&lt;p&gt;We're excited to see what you build with it!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Fetch PHP is an open-source project. We welcome contributions, feedback, and feature requests on our &lt;a href="https://github.com/Thavarshan/fetch-php" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>php</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Type-Safe Form Handling in Laravel + Vue.js with Formlink</title>
      <dc:creator>Jerome Thayananthajothy</dc:creator>
      <pubDate>Fri, 18 Oct 2024 17:21:57 +0000</pubDate>
      <link>https://dev.to/thavarshan/type-safe-form-handling-in-laravel-vuejs-with-formlink-3a7a</link>
      <guid>https://dev.to/thavarshan/type-safe-form-handling-in-laravel-vuejs-with-formlink-3a7a</guid>
      <description>&lt;p&gt;Forms are the backbone of web applications, yet handling them properly can be surprisingly complex. From validation to file uploads, CSRF protection to progress tracking, there's a lot that can go wrong. That's where Formlink comes in - a TypeScript-first form handling library that bridges Laravel and Vue.js applications with elegant simplicity.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Traditional Form Handling
&lt;/h2&gt;

&lt;p&gt;Traditional form handling often involves a lot of boilerplate code. You need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manually track form state&lt;/li&gt;
&lt;li&gt;Handle validation errors&lt;/li&gt;
&lt;li&gt;Deal with file uploads&lt;/li&gt;
&lt;li&gt;Manage CSRF tokens&lt;/li&gt;
&lt;li&gt;Track submission progress&lt;/li&gt;
&lt;li&gt;Type everything correctly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's look at how Formlink solves these challenges with its type-safe approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started with Formlink
&lt;/h2&gt;

&lt;p&gt;First, install Formlink using your preferred package manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;formlink
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The magic of Formlink lies in its TypeScript integration. Let's start with a simple example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ContactForm&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useForm&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ContactForm&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By defining an interface, we get complete type safety throughout our form handling. Try to assign a number to the email field? TypeScript will catch that error before it ever reaches your users.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Power of Type-Safe Validation
&lt;/h2&gt;

&lt;p&gt;One of the most powerful features of Formlink is its type-safe validation system. Here's how it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ValidationRule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ValidationRules&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;]?:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ValidationRule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This type system ensures that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You can only validate fields that actually exist in your form&lt;/li&gt;
&lt;li&gt;Validation rules are properly structured&lt;/li&gt;
&lt;li&gt;Error messages are always present&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Real-World Example
&lt;/h2&gt;

&lt;p&gt;Let's look at a complete example that showcases Formlink's capabilities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;submit.prevent=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"form.email"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"form.errors.email"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;change=&lt;/span&gt;&lt;span class="s"&gt;"handleFile"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"form.progress"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"progress"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;percentage&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;% uploaded
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;:disabled=&lt;/span&gt;&lt;span class="s"&gt;"form.processing"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;processing&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sending...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Send&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Features That Make Life Easier
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Progress Tracking
&lt;/h3&gt;

&lt;p&gt;Formlink provides built-in progress tracking for file uploads. No more guessing when your files will finish uploading:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/upload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;onProgress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;percentage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;% uploaded`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Form Transformation
&lt;/h3&gt;

&lt;p&gt;Need to clean up your data before submission? Formlink's transform method has you covered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reactive Form State
&lt;/h3&gt;

&lt;p&gt;Track your form's state effortlessly with reactive properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;form.processing&lt;/code&gt;: Is the form being submitted?&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;form.progress&lt;/code&gt;: Track upload progress&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;form.errors&lt;/code&gt;: Access validation errors&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;form.isDirty&lt;/code&gt;: Has the form been modified?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Choose Formlink?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Type Safety&lt;/strong&gt;: Full TypeScript support means fewer runtime errors and better developer experience&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Configuration&lt;/strong&gt;: Works out of the box with Laravel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in CSRF Protection&lt;/strong&gt;: No need to manually handle CSRF tokens&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Progress Tracking&lt;/strong&gt;: Real-time file upload progress without extra code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart Error Handling&lt;/strong&gt;: Automatic Laravel validation error handling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framework Agnostic&lt;/strong&gt;: While optimized for Laravel, it works with any backend&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Performance Considerations
&lt;/h2&gt;

&lt;p&gt;Formlink is designed to be lightweight and efficient. It only includes what you need and nothing more. The TypeScript-first approach means you catch errors at compile time rather than runtime, leading to more reliable applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Form handling doesn't have to be complicated. With Formlink, you get a type-safe, feature-rich solution that makes form handling in Laravel + Vue.js applications a breeze. The combination of TypeScript's type safety and Formlink's intuitive API means you can focus on building features rather than fighting with form handling.&lt;/p&gt;

&lt;p&gt;Try it out in your next project, and experience the difference that proper type-safe form handling can make. Your future self (and your team) will thank you.&lt;/p&gt;

&lt;p&gt;Remember, good form handling is about more than just sending data to the server - it's about providing a smooth, error-free experience for your users while maintaining developer sanity. Formlink helps you achieve both.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>laravel</category>
      <category>vue</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Meet phpvm: Your New PHP Best Friend Forever!</title>
      <dc:creator>Jerome Thayananthajothy</dc:creator>
      <pubDate>Sat, 05 Oct 2024 22:41:25 +0000</pubDate>
      <link>https://dev.to/thavarshan/meet-phpvm-your-new-php-best-friend-forever-m3n</link>
      <guid>https://dev.to/thavarshan/meet-phpvm-your-new-php-best-friend-forever-m3n</guid>
      <description>&lt;p&gt;Howdy, PHP aficionados and version-juggling ninjas! Are you tired of the PHP version tango? You know, that dance where you're constantly switching between PHP versions faster than a cat video goes viral? Well, put on your party hats because I've got some exciting news that'll make your development life easier (and dare I say, more fun)!&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing phpvm: The PHP Version Manager of Your Dreams
&lt;/h2&gt;

&lt;p&gt;Picture this: You're working on a legacy project that runs on PHP 7.2, but your shiny new side project is built on PHP 8.1. Normally, this would be the part where you pull your hair out and question your life choices. But not anymore! Enter &lt;strong&gt;phpvm&lt;/strong&gt;, your new PHP version management bestie.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's the Big Deal?
&lt;/h2&gt;

&lt;p&gt;phpvm is like a magic wand for PHP versions. With just a few simple commands, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install multiple PHP versions faster than you can say "Laravel"&lt;/li&gt;
&lt;li&gt;Switch between PHP versions smoother than a barista makes a latte&lt;/li&gt;
&lt;li&gt;Manage your PHP versions like a boss, without breaking a sweat&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Get This Magic in Your Life
&lt;/h2&gt;

&lt;p&gt;Getting started with phpvm is easier than explaining why you need another JavaScript framework. Just run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-o-&lt;/span&gt; https://raw.githubusercontent.com/Thavarshan/phpvm/0.0.1/bin/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or if you're more of a wget person:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget &lt;span class="nt"&gt;-qO-&lt;/span&gt; https://raw.githubusercontent.com/Thavarshan/phpvm/0.0.1/bin/install | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And voilà! You're ready to rock the PHP version management world!&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's See This Baby in Action!
&lt;/h2&gt;

&lt;p&gt;Want to install PHP 8.1? Easy peasy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phpvm &lt;span class="nb"&gt;install &lt;/span&gt;8.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Need to switch to PHP 7.4 for that legacy project? No problemo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phpvm use 7.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Want to see all your installed PHP versions? We've got you covered:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;It's so simple, even your cat could do it (if they had opposable thumbs, that is).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why You'll Love phpvm
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;It's fast&lt;/strong&gt;: Switch PHP versions faster than you can say "deprecated function".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's flexible&lt;/strong&gt;: Works on Unix-like systems, including macOS and Linux.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's friendly&lt;/strong&gt;: No need for sudo hugs – phpvm plays nice with your system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's fantastic&lt;/strong&gt;: Manage global PHP settings and extensions like a pro.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Join the phpvm Party!
&lt;/h2&gt;

&lt;p&gt;Now, I know what you're thinking: "This sounds too good to be true!" But trust me, it's real, and it's spectacular. And the best part? It's open source!&lt;/p&gt;

&lt;p&gt;So, here's what you can do to join in on the fun:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Star the repository&lt;/strong&gt;: Show some love on &lt;a href="https://github.com/Thavarshan/phpvm" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. It's like a high-five for code!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Found a bug?&lt;/strong&gt; Open an issue! It's like playing detective, but for code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Have an idea?&lt;/strong&gt; Submit a pull request! It's like bringing a dish to a potluck, but the dish is code, and the potluck is our GitHub repo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spread the word&lt;/strong&gt;: Tell your friends, your colleagues, your rubber duck – everyone needs to know about phpvm!&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Final Act
&lt;/h2&gt;

&lt;p&gt;Say goodbye to PHP version headaches and hello to smooth sailing with phpvm. It's time to make PHP version management as enjoyable as binge-watching your favorite TV show.&lt;/p&gt;

&lt;p&gt;So, what are you waiting for? Dive in, give phpvm a spin, and let's make PHP development great again!&lt;/p&gt;

&lt;p&gt;Remember, with great power comes great responsibility... to have fun while coding! Happy PHP-ing, folks!&lt;/p&gt;

</description>
      <category>php</category>
      <category>nvm</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>FetchPHP 2.0: An Update To Bring Javascript's Fetch &amp; True Async To PHP (Now With Superpowers!)</title>
      <dc:creator>Jerome Thayananthajothy</dc:creator>
      <pubDate>Mon, 30 Sep 2024 22:14:39 +0000</pubDate>
      <link>https://dev.to/thavarshan/fetchphp-20-an-update-to-bring-javascripts-fetch-true-async-to-php-now-with-superpowers-3g1m</link>
      <guid>https://dev.to/thavarshan/fetchphp-20-an-update-to-bring-javascripts-fetch-true-async-to-php-now-with-superpowers-3g1m</guid>
      <description>&lt;p&gt;Hello lovely people!&lt;/p&gt;

&lt;p&gt;We are thrilled to announce the release of FetchPHP 2.0! This new version is crafted to simplify HTTP requests in PHP, enhancing your development experience with greater ease and efficiency. If you have ever faced challenges with asynchronous operations in PHP, FetchPHP 2.0 is the perfect solution for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Journey: From FetchPHP to a More Powerful Version
&lt;/h2&gt;

&lt;p&gt;The inception of FetchPHP stemmed from a straightforward observation: PHP developers deserve the same ease of use that JavaScript developers enjoy with fetch. FetchPHP was created to bring the simplicity of JavaScript’s HTTP handling to the PHP ecosystem. But innovation didn't stop at version 1.0. With the release of FetchPHP 2.0, we've taken it a step further. This version not only simplifies the use of HTTP clients but also complements PHP's native asynchronous functionality (&lt;a href="https://www.php.net/manual/en/language.fibers.php" rel="noopener noreferrer"&gt;PHP Fibers&lt;/a&gt;), providing a tool that truly transforms the way you handle HTTP requests in PHP.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s New in FetchPHP 2.0?
&lt;/h2&gt;

&lt;p&gt;FetchPHP 2.0 is packed with powerful features aimed at enhancing both simplicity and performance:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;True Asynchronous Execution&lt;/strong&gt;: FetchPHP 2.0 introduces true non-blocking asynchronous operations by utilizing PHP Fibers, thanks to the Matrix package. This advancement provides concurrency in PHP like never before, making your applications faster and more efficient.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;JavaScript-Like Syntax&lt;/strong&gt;: Developers coming from a JavaScript background will find FetchPHP’s new &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;-like syntax very familiar, allowing for seamless transitions between languages.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fluent API&lt;/strong&gt;: Inspired by Laravel’s HTTP client, FetchPHP’s fluent API allows you to build and chain HTTP requests effortlessly, ensuring clarity and simplicity in even the most complex operations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enhanced Error Handling&lt;/strong&gt;: FetchPHP 2.0 provides customizable error management through an improved &lt;code&gt;ErrorHandler&lt;/code&gt;. Whether you need to retry, pause, resume, or cancel requests, the control is entirely in your hands.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Synchronous Requests with Guzzle&lt;/strong&gt;: While FetchPHP’s asynchronous capabilities are powered by Matrix, synchronous requests are managed with the reliable Guzzle HTTP client, ensuring robust performance across all request types.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Advanced Features for Modern Development
&lt;/h2&gt;

&lt;p&gt;FetchPHP 2.0 is designed to handle more complex scenarios without compromising on simplicity. Consider this example:&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="nf"&gt;async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&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;fetch&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;baseUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://api.example.com'&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;withHeaders&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'X-API-Key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'your-key'&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;withBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'data'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'value'&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;withProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'tcp://localhost:8080'&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;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&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;retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&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;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/endpoint'&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;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&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;handleResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$e&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;logError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This demonstrates the power of FetchPHP 2.0 to handle asynchronous requests, proxies, retries, timeouts, and more—all while maintaining clean, readable code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Choose FetchPHP Over Guzzle?
&lt;/h2&gt;

&lt;p&gt;Let’s compare FetchPHP 2.0 with Guzzle to see how FetchPHP brings unique advantages to PHP developers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Asynchronous Execution&lt;/strong&gt;: Guzzle uses promises for async operations, while FetchPHP takes advantage of PHP Fibers, providing true non-blocking concurrency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Familiar Syntax&lt;/strong&gt;: FetchPHP offers a JavaScript-like syntax, making it intuitive for developers who are familiar with modern JavaScript or who prefer a streamlined, less verbose approach.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced Error Handling&lt;/strong&gt;: FetchPHP’s &lt;code&gt;ErrorHandler&lt;/code&gt; offers flexible control over error states, allowing developers to manage requests dynamically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ease of Use&lt;/strong&gt;: FetchPHP’s fluent API simplifies complex HTTP operations, providing a more natural and elegant way to structure requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A Practical Example: Guzzle vs. FetchPHP
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Guzzle example&lt;/span&gt;
&lt;span class="nv"&gt;$promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;requestAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'http://httpbin.org/get'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$promise&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;then&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="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Request completed: '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getBody&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// FetchPHP example&lt;/span&gt;
&lt;span class="nf"&gt;async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'http://httpbin.org/get'&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;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Request completed: '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Request failed: '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;FetchPHP not only reduces boilerplate code but also introduces a more natural way of handling asynchronous operations in PHP.&lt;/p&gt;

&lt;h2&gt;
  
  
  FetchPHP 2.0: A New Era of PHP HTTP Requests
&lt;/h2&gt;

&lt;p&gt;FetchPHP 2.0 is a powerful, flexible, and intuitive tool that simplifies the process of handling HTTP requests in PHP. Whether you are building small APIs or managing large-scale systems that require thousands of concurrent requests, FetchPHP has the capabilities to enhance your project.&lt;/p&gt;

&lt;p&gt;Head over to our &lt;a href="https://github.com/Thavarshan/fetch-php" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; to explore FetchPHP, contribute to the project, and don't forget to give us a star on the repository to show your support. Let’s move the PHP ecosystem forward, one asynchronous request at a time.&lt;/p&gt;

&lt;p&gt;With FetchPHP 2.0, we’re not just improving how you make HTTP requests in PHP—we’re redefining it. Take control of your HTTP operations with true asynchronous capabilities, a developer-friendly API, and powerful error handling. This is more than an update. It’s a revolution.&lt;/p&gt;

&lt;p&gt;Explore the future of PHP HTTP requests with FetchPHP 2.0.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>php</category>
      <category>http</category>
    </item>
    <item>
      <title>Exploring Matrix: Bringing JavaScript-like Async to PHP</title>
      <dc:creator>Jerome Thayananthajothy</dc:creator>
      <pubDate>Sun, 29 Sep 2024 23:54:49 +0000</pubDate>
      <link>https://dev.to/thavarshan/exploring-matrix-bringing-javascript-like-async-to-php-4e47</link>
      <guid>https://dev.to/thavarshan/exploring-matrix-bringing-javascript-like-async-to-php-4e47</guid>
      <description>&lt;p&gt;Are you a PHP developer who's envied the simplicity of JavaScript's &lt;code&gt;async/await&lt;/code&gt; paradigm? Do you wish you could manage asynchronous tasks in PHP with the same ease? Look no further! Today, we're diving deep into Matrix, a cutting-edge PHP library that brings the power of JavaScript-like async operations to the PHP ecosystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Matrix?
&lt;/h2&gt;

&lt;p&gt;Matrix is an innovative PHP library designed for asynchronous task management. It draws inspiration from JavaScript's &lt;code&gt;async/await&lt;/code&gt; paradigm but leverages PHP's native Fibers to provide true non-blocking concurrency. With Matrix, you can run tasks, manage errors, and handle results—all without the need for explicit task starting.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Power of PHP Fibers
&lt;/h2&gt;

&lt;p&gt;At the heart of Matrix lies PHP Fibers, a feature introduced in PHP 8.1. But what exactly are Fibers, and how do they revolutionize asynchronous programming in PHP?&lt;/p&gt;

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

&lt;p&gt;Fibers in PHP are lightweight cooperative concurrency primitives. They allow you to create sections of code that can be paused and resumed, enabling cooperative multitasking within a single PHP thread. This is a game-changer for handling I/O-bound operations, as it allows other code to run while waiting for slow operations to complete.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Fibers Power Matrix
&lt;/h3&gt;

&lt;p&gt;Matrix harnesses the power of Fibers to create a non-blocking, concurrent execution environment. When you create a task in Matrix, it's wrapped in a Fiber. This allows the task to be suspended and resumed without blocking the entire PHP process, enabling true asynchronous behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture of Matrix
&lt;/h2&gt;

&lt;p&gt;Matrix is built around several key components that work together to provide a seamless async experience:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AsyncHelper&lt;/strong&gt;: This class provides the JavaScript-like async API, allowing you to chain &lt;code&gt;then()&lt;/code&gt; and &lt;code&gt;catch()&lt;/code&gt; methods.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task&lt;/strong&gt;: The core unit of work in Matrix, representing an operation that can be started, paused, resumed, or canceled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handler&lt;/strong&gt;: Manages error handling and retries, providing robust fault tolerance.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These components are designed to work seamlessly together, providing a cohesive and intuitive API for managing asynchronous operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Using Matrix
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Simplified Async Code&lt;/strong&gt;: Write cleaner, more readable asynchronous code that closely resembles JavaScript's &lt;code&gt;async/await&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;True Concurrency&lt;/strong&gt;: Leverage PHP Fibers for non-blocking execution of multiple tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible Error Handling&lt;/strong&gt;: Customize error recovery strategies with the powerful &lt;code&gt;Handler&lt;/code&gt; class.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine-grained Control&lt;/strong&gt;: Manage task lifecycles with methods for pausing, resuming, and canceling tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Familiar API&lt;/strong&gt;: If you're coming from a JavaScript background, you'll feel right at home with Matrix's API.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Usage Example
&lt;/h2&gt;

&lt;p&gt;Let's look at a simple example of how you can use Matrix in your PHP projects:&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;Matrix\AsyncHelper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;Matrix\async&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Define an asynchronous task&lt;/span&gt;
&lt;span class="nv"&gt;$task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;async&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="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Simulate a time-consuming operation&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"Task completed successfully!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Handle the task result&lt;/span&gt;
&lt;span class="nv"&gt;$task&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;then&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="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Output: Task completed successfully!&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;catch&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="nv"&gt;$error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"An error occurred: "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$error&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we define an asynchronous task using the &lt;code&gt;async&lt;/code&gt; function. The task simulates a time-consuming operation with &lt;code&gt;yield&lt;/code&gt;. We then use the &lt;code&gt;then()&lt;/code&gt; method to handle the successful result and &lt;code&gt;catch()&lt;/code&gt; to handle any errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Improvements
&lt;/h2&gt;

&lt;p&gt;While Matrix already offers a powerful set of features, there's always room for growth. Some potential areas for future improvement include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Parallel Execution&lt;/strong&gt;: Implement methods for running multiple tasks in parallel and aggregating their results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration with Popular Frameworks&lt;/strong&gt;: Develop plugins or extensions for seamless integration with popular PHP frameworks like Laravel or Symfony.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Optimizations&lt;/strong&gt;: Continually refine the underlying Fiber management for even better performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extended Monitoring and Debugging Tools&lt;/strong&gt;: Implement more advanced tools for monitoring task execution and debugging async flows.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Matrix represents a significant step forward in PHP's async capabilities. By bringing a JavaScript-like async experience to PHP and leveraging the power of Fibers, it offers developers a powerful new tool for managing asynchronous operations.&lt;/p&gt;

&lt;p&gt;Whether you're building a high-performance API, managing complex background jobs, or simply looking to write cleaner async code, Matrix deserves a place in your PHP toolbox. Give it a try in your next project and experience the future of asynchronous PHP programming today!&lt;/p&gt;

&lt;p&gt;Ready to get started? Check out the &lt;a href="https://github.com/Thavarshan/matrix" rel="noopener noreferrer"&gt;Matrix GitHub repository&lt;/a&gt; and join the async revolution in PHP!&lt;/p&gt;

</description>
      <category>matrix</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>php</category>
    </item>
    <item>
      <title>Effortless Secret Management for Laravel &amp; JS Projects with Secrets Loader</title>
      <dc:creator>Jerome Thayananthajothy</dc:creator>
      <pubDate>Thu, 19 Sep 2024 18:04:34 +0000</pubDate>
      <link>https://dev.to/thavarshan/effortless-secret-management-for-laravel-js-projects-with-secrets-loader-59be</link>
      <guid>https://dev.to/thavarshan/effortless-secret-management-for-laravel-js-projects-with-secrets-loader-59be</guid>
      <description>&lt;p&gt;Managing sensitive data like API keys, tokens, and credentials across various environments can be quite tricky, especially when developing and deploying applications. Ensuring secrets are securely stored and fetched when needed, without hardcoding them into version control, is crucial for maintaining security.&lt;/p&gt;

&lt;p&gt;That's why I created &lt;strong&gt;Secrets Loader&lt;/strong&gt;, a Bash script that dynamically fetches secrets from AWS SSM and CloudFormation directly into your &lt;code&gt;.env&lt;/code&gt; file, making local development and deployment easier, safer, and more efficient.&lt;/p&gt;




&lt;h3&gt;
  
  
  What is Secrets Loader?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Secrets Loader&lt;/strong&gt; is a simple tool designed to automatically fetch secrets from AWS SSM Parameter Store and AWS CloudFormation outputs based on custom syntax in your &lt;code&gt;.env&lt;/code&gt; file. It replaces placeholders with actual secrets without ever exposing sensitive information in version control.&lt;/p&gt;

&lt;p&gt;For example, instead of hardcoding your API keys or credentials, you define them in your &lt;code&gt;.env&lt;/code&gt; file like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;THIRD_PARTY_API_KEY="ssm:/third-party/api/key"
AWS_ACCESS_KEY_ID="cf:my-stack:AccessKeyId"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a single command, Secrets Loader will fetch the actual values from AWS and update your &lt;code&gt;.env&lt;/code&gt; file, keeping sensitive information secure and easy to manage.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why I Built It
&lt;/h3&gt;

&lt;p&gt;During local development and deployment, I found myself dealing with sensitive credentials that I didn't want hardcoded into the project files. Having used AWS services extensively, I wanted a way to integrate secret management into my existing development workflow without too much hassle.&lt;/p&gt;

&lt;p&gt;Here are the main challenges Secrets Loader solves:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Avoiding hardcoded secrets&lt;/strong&gt;: No more committing secrets to version control. You can safely use placeholders and dynamically fetch the actual values from AWS SSM and CloudFormation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reducing manual effort&lt;/strong&gt;: Instead of manually copying and pasting secret values, just define them once in your &lt;code&gt;.env&lt;/code&gt; file, and let the script do the fetching.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplifying secret management&lt;/strong&gt;: Whether you're working in local development, staging, or production, Secrets Loader ensures that secrets are securely and automatically loaded.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Secrets Loader&lt;/strong&gt; comes with a few key features that make it a handy tool for both local development and production environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automated secret loading&lt;/strong&gt;: Fetch secrets from AWS SSM Parameter Store and CloudFormation by specifying paths in your &lt;code&gt;.env&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security-first approach&lt;/strong&gt;: Keep sensitive data out of version control by securely loading it at runtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple syntax&lt;/strong&gt;: Use a custom syntax in your &lt;code&gt;.env&lt;/code&gt; file (&lt;code&gt;ssm:&lt;/code&gt; for SSM parameters, &lt;code&gt;cf:&lt;/code&gt; for CloudFormation outputs) to specify where secrets should come from.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error handling&lt;/strong&gt;: The script continues to process other secrets even if one retrieval fails, logging warnings without stopping your workflow.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;p&gt;The magic of &lt;strong&gt;Secrets Loader&lt;/strong&gt; lies in its ability to fetch secrets from AWS based on specific prefixes (&lt;code&gt;ssm:&lt;/code&gt; and &lt;code&gt;cf:&lt;/code&gt;). Here's an example workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Set up your &lt;code&gt;.env&lt;/code&gt; file&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Add placeholders for your secrets in your &lt;code&gt;.env&lt;/code&gt; file using the &lt;code&gt;ssm:&lt;/code&gt; prefix for SSM parameters or the &lt;code&gt;cf:&lt;/code&gt; prefix for CloudFormation outputs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   THIRD_PARTY_API_KEY="ssm:/third-party/api/key"
   AWS_SECRET_ACCESS_KEY="cf:my-stack:SecretAccessKey"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run the script&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Use the following command to run the script and fetch the secrets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   ./secrets.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Updated &lt;code&gt;.env&lt;/code&gt; file&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After running the script, your &lt;code&gt;.env&lt;/code&gt; file will be updated with the actual values fetched from AWS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   THIRD_PARTY_API_KEY=actual-api-key-value
   AWS_SECRET_ACCESS_KEY=actual-access-key-value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No more hardcoding secrets, and no more manual lookups!&lt;/p&gt;




&lt;h3&gt;
  
  
  Installation &amp;amp; Setup
&lt;/h3&gt;

&lt;p&gt;Ready to get started? Here's how you can set up &lt;strong&gt;Secrets Loader&lt;/strong&gt; in your project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clone the repository&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   git clone https://github.com/Thavarshan/secretst-loader.git
   &lt;span class="nb"&gt;cd &lt;/span&gt;secretst-loader
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Make the script executable&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;chmod&lt;/span&gt; +x secrets.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ensure AWS CLI is installed and configured&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you don’t have the AWS CLI installed, follow the &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html" rel="noopener noreferrer"&gt;AWS CLI installation guide&lt;/a&gt;. After installing, configure your AWS credentials:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Define your secrets in &lt;code&gt;.env&lt;/code&gt;&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Use the &lt;code&gt;ssm:&lt;/code&gt; and &lt;code&gt;cf:&lt;/code&gt; prefixes to define where secrets should come from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   THIRD_PARTY_API_KEY="ssm:/third-party/api/key"
   AWS_ACCESS_KEY_ID="cf:my-stack:AccessKeyId"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Example Usage
&lt;/h3&gt;

&lt;p&gt;Let’s take a look at a simple example:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;.env.example&lt;/code&gt; File:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Application settings
APP_NAME=MyApp
APP_ENV=production

# Secrets fetched from AWS SSM and CloudFormation
THIRD_PARTY_API_KEY="ssm:/third-party/api/key"
AWS_SECRET_ACCESS_KEY="cf:my-stack:SecretAccessKey"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Running Secrets Loader:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./secrets.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Updated &lt;code&gt;.env&lt;/code&gt; File:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Application settings
APP_NAME=MyApp
APP_ENV=production

# Fetched secrets
THIRD_PARTY_API_KEY=actual-api-key-value
AWS_SECRET_ACCESS_KEY=actual-secret-access-key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Troubleshooting
&lt;/h3&gt;

&lt;p&gt;If you encounter any issues while using &lt;strong&gt;Secrets Loader&lt;/strong&gt;, here are a few things to check:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AWS Permissions&lt;/strong&gt;: Ensure that the AWS CLI is configured correctly and that your IAM role or user has sufficient permissions to access AWS SSM and CloudFormation secrets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Syntax Errors&lt;/strong&gt;: Double-check the syntax in your &lt;code&gt;.env&lt;/code&gt; file to make sure the &lt;code&gt;ssm:&lt;/code&gt; and &lt;code&gt;cf:&lt;/code&gt; prefixes are correct.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Script Errors&lt;/strong&gt;: If the script fails to fetch certain secrets, it will log warnings but continue fetching the others. Review the logs for any error messages and make sure the AWS resources exist and are accessible.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Extending Secrets Loader
&lt;/h3&gt;

&lt;p&gt;The script is designed to be extensible. If you'd like to integrate other secret management systems (like Azure Key Vault or HashiCorp Vault), you can easily modify the script to support new prefixes and fetch logic.&lt;/p&gt;

&lt;p&gt;For example, you could add an &lt;code&gt;azkv:&lt;/code&gt; prefix to fetch secrets from Azure Key Vault and handle the retrieval using the Azure CLI.&lt;/p&gt;




&lt;h3&gt;
  
  
  Contributing
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Secrets Loader&lt;/strong&gt; is open-source, and contributions are always welcome! If you'd like to add features, fix bugs, or suggest improvements, feel free to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open an issue&lt;/strong&gt;: Share your feedback or bug reports.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Submit a pull request&lt;/strong&gt;: Contribute code by following our &lt;a href="https://github.com/Thavarshan/secrets-loader/blob/main/CONTRIBUTING.md" rel="noopener noreferrer"&gt;CONTRIBUTING guidelines&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;If you're tired of manually managing secrets across environments, &lt;strong&gt;Secrets Loader&lt;/strong&gt; is a simple, effective tool to streamline the process. By fetching secrets dynamically from AWS SSM and CloudFormation, you can securely manage your credentials without risking exposure in version control.&lt;/p&gt;

&lt;p&gt;Check out the project on &lt;a href="https://github.com/Thavarshan/secrets-loader" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, give it a try, and if you find it useful, &lt;strong&gt;give us a ⭐ on GitHub!&lt;/strong&gt; Your support helps the project grow, and we'd love to hear your feedback or see your contributions to its ongoing development.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>aws</category>
      <category>javascript</category>
      <category>development</category>
    </item>
  </channel>
</rss>
