<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://bhupendranegi.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://bhupendranegi.github.io/" rel="alternate" type="text/html" /><updated>2026-06-14T15:24:21+00:00</updated><id>https://bhupendranegi.github.io/feed.xml</id><title type="html">Bhupendra Negi</title><subtitle>Software Engineer specialising in Ruby on Rails and React</subtitle><entry><title type="html">Ruby on Rails vs Node.js vs Go</title><link href="https://bhupendranegi.github.io/Rails-vs-Node-vs-Go/" rel="alternate" type="text/html" title="Ruby on Rails vs Node.js vs Go" /><published>2026-01-10T00:00:00+00:00</published><updated>2026-01-10T00:00:00+00:00</updated><id>https://bhupendranegi.github.io/Rails-vs-Node-vs-Go</id><content type="html" xml:base="https://bhupendranegi.github.io/Rails-vs-Node-vs-Go/"><![CDATA[<hr />
<p>“Which backend should I use?” is one of the most-argued questions in software — and most answers pick a single metric (usually raw speed) and declare a winner. That’s the wrong lens. The right backend depends on what you’re building, how fast you need to ship, and who you can hire. Let’s compare Ruby on Rails, Node.js, and Go across the dimensions that actually move a project.</p>

<figure class="post-figure">
  <div class="post-figure-compare">
    <div class="post-figure-step">
      <span class="post-figure-icons">
        <i class="devicon-ruby-plain" aria-hidden="true"></i>
        <i class="devicon-rails-plain" aria-hidden="true"></i>
        <i class="devicon-postgresql-plain" aria-hidden="true"></i>
      </span>
      <span class="post-figure-step-label">Ruby on Rails</span>
      <span class="post-figure-step-sub">Convention-driven. Fastest path to a business-logic-heavy product.</span>
    </div>
    <div class="post-figure-step">
      <span class="post-figure-icons">
        <i class="devicon-nodejs-plain" aria-hidden="true"></i>
        <i class="devicon-javascript-plain" aria-hidden="true"></i>
        <i class="devicon-express-original" aria-hidden="true"></i>
      </span>
      <span class="post-figure-step-label">Node.js</span>
      <span class="post-figure-step-sub">JavaScript everywhere. Great at real-time and shared front/back code.</span>
    </div>
    <div class="post-figure-step">
      <span class="post-figure-icons">
        <i class="devicon-go-plain" aria-hidden="true"></i>
        <i class="devicon-docker-plain" aria-hidden="true"></i>
        <i class="devicon-kubernetes-plain" aria-hidden="true"></i>
      </span>
      <span class="post-figure-step-label">Go</span>
      <span class="post-figure-step-sub">Compiled &amp; concurrent. Built for high-throughput, low-overhead systems.</span>
    </div>
  </div>
  <figcaption class="post-figure-caption">Three strong backends, three different sweet spots.</figcaption>
</figure>

<h5 id="one-benchmark-wont-decide-it">One benchmark won’t decide it</h5>

<p>A framework that wins a “requests per second” chart can still be the wrong choice if it takes twice as long to build features or you can’t hire for it. Engineering decisions are trade-offs across speed-to-market, running cost, hiring, and maintainability — so weigh all of them, not just the one that’s easy to measure.</p>

<h5 id="performance-and-concurrency">Performance and concurrency</h5>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Go:</code> The clear winner on raw throughput. It’s compiled, and goroutines make concurrency cheap, so it often needs significantly less compute for the same load. Ideal for proxies, APIs at scale, and CPU-bound work.</li>
  <li><code class="language-plaintext highlighter-rouge">Node.js:</code> A non-blocking event loop makes it excellent for I/O-bound, real-time workloads — chat, streaming, live dashboards — though CPU-heavy tasks block that single thread.</li>
  <li><code class="language-plaintext highlighter-rouge">Rails:</code> Not built for raw benchmarks, but rarely the bottleneck for typical web apps. With background jobs and caching, it scales comfortably for the vast majority of products.</li>
</ul>

<h5 id="fastest-to-an-mvp">Fastest to an MVP</h5>

<p>This is where Rails earns its reputation. Convention over configuration means less boilerplate and fewer decisions — generators, an ORM, migrations, and a mature testing story are all built in. Node.js is fast too, but you assemble your own stack from many packages. Go is the most deliberate: explicit and verbose by design, which trades early speed for long-term clarity.</p>

<h5 id="the-feel-of-the-code">The feel of the code</h5>

<p>A trivial “create a user” endpoint shows the ergonomics of each:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Rails</span>
<span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
  <span class="k">def</span> <span class="nf">create</span>
    <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">create!</span><span class="p">(</span><span class="n">user_params</span><span class="p">)</span>
    <span class="n">render</span> <span class="ss">json: </span><span class="n">user</span><span class="p">,</span> <span class="ss">status: :created</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Node.js (Express)</span>
<span class="nx">app</span><span class="p">.</span><span class="nf">post</span><span class="p">(</span><span class="dl">"</span><span class="s2">/users</span><span class="dl">"</span><span class="p">,</span> <span class="k">async </span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">user</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">User</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="nx">req</span><span class="p">.</span><span class="nx">body</span><span class="p">)</span>
  <span class="nx">res</span><span class="p">.</span><span class="nf">status</span><span class="p">(</span><span class="mi">201</span><span class="p">).</span><span class="nf">json</span><span class="p">(</span><span class="nx">user</span><span class="p">)</span>
<span class="p">})</span>
</code></pre></div></div>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// Go (net/http)</span>
<span class="k">func</span> <span class="n">createUser</span><span class="p">(</span><span class="n">w</span> <span class="n">http</span><span class="o">.</span><span class="n">ResponseWriter</span><span class="p">,</span> <span class="n">r</span> <span class="o">*</span><span class="n">http</span><span class="o">.</span><span class="n">Request</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">var</span> <span class="n">u</span> <span class="n">User</span>
    <span class="n">json</span><span class="o">.</span><span class="n">NewDecoder</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">Body</span><span class="p">)</span><span class="o">.</span><span class="n">Decode</span><span class="p">(</span><span class="o">&amp;</span><span class="n">u</span><span class="p">)</span>
    <span class="n">db</span><span class="o">.</span><span class="n">Create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">u</span><span class="p">)</span>
    <span class="n">w</span><span class="o">.</span><span class="n">WriteHeader</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">StatusCreated</span><span class="p">)</span>
    <span class="n">json</span><span class="o">.</span><span class="n">NewEncoder</span><span class="p">(</span><span class="n">w</span><span class="p">)</span><span class="o">.</span><span class="n">Encode</span><span class="p">(</span><span class="n">u</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<h5 id="hiring-and-cost">Hiring and cost</h5>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Node.js:</code> The largest talent pool — most developers know JavaScript — but skill levels vary widely and architectures differ from team to team.</li>
  <li><code class="language-plaintext highlighter-rouge">Rails:</code> A smaller but specialised pool. Because the framework is so opinionated, Rails developers tend to be productive in an unfamiliar codebase quickly, which makes hiring outcomes more predictable.</li>
  <li><code class="language-plaintext highlighter-rouge">Go:</code> A smaller, more senior pool. Great engineers, but typically a higher rate and a longer search.</li>
</ul>

<h5 id="ecosystem-and-dependencies">Ecosystem and dependencies</h5>

<p>Rails gives you a curated, batteries-included ecosystem — most of what a web app needs is one gem away. Node.js has the largest registry by far, but that breadth brings “dependency churn”: fast-moving, sometimes shallow packages. Go leans on a strong standard library and keeps dependencies minimal, which pays off in long-term stability.</p>

<h5 id="when-to-choose-each">When to choose each</h5>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Choose Rails</code> for SaaS products, marketplaces, and business systems where feature velocity and maintainability matter most.</li>
  <li><code class="language-plaintext highlighter-rouge">Choose Node.js</code> when real-time features are central, or you want to share code and people across the front and back end.</li>
  <li><code class="language-plaintext highlighter-rouge">Choose Go</code> for performance-critical infrastructure — high-throughput APIs, proxies, and services where compute cost is the dominant concern.</li>
</ul>

<h5 id="conclusion">Conclusion</h5>

<p>There’s no universal winner. Go wins on raw efficiency, Node.js on real-time and a shared language, and Rails on getting a feature-rich product to market — and keeping it maintainable for years. For most business-logic-heavy applications, that last quality is the one that compounds, which is why Rails remains such a strong default. Pick the tool that fits the job in front of you, not the one that tops a benchmark.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[“Which backend should I use?” is one of the most-argued questions in software — and most answers pick a single metric (usually raw speed) and declare a winner. That’s the wrong lens. The right backend depends on what you’re building, how fast you need to ship, and who you can hire. Let’s compare Ruby on Rails, Node.js, and Go across the dimensions that actually move a project.]]></summary></entry><entry><title type="html">Upgrading Rails 5 &amp;amp; 6 to Rails 8</title><link href="https://bhupendranegi.github.io/Upgrading-Rails-5-6-to-Rails-8/" rel="alternate" type="text/html" title="Upgrading Rails 5 &amp;amp; 6 to Rails 8" /><published>2025-05-22T00:00:00+00:00</published><updated>2025-05-22T00:00:00+00:00</updated><id>https://bhupendranegi.github.io/Upgrading-Rails-5-6-to-Rails-8</id><content type="html" xml:base="https://bhupendranegi.github.io/Upgrading-Rails-5-6-to-Rails-8/"><![CDATA[<hr />
<p>Staying on an old Rails version feels safe, but it quietly piles up risk: missing security patches, a shrinking pool of developers who want to work on it, and features you simply can’t use. The good news is that upgrading is a well-trodden path — as long as you do it <strong>one major version at a time</strong> and lean on the tooling Rails gives you. Here’s a practical route from Rails 5/6 all the way to Rails 8.</p>

<figure class="post-figure">
  <div class="post-figure-flow">
    <div class="post-figure-step">
      <i data-lucide="package" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Rails 5.2</span>
      <span class="post-figure-step-sub">Your baseline</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="package" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Rails 6.1</span>
      <span class="post-figure-step-sub">Zeitwerk, multi-DB</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="package" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Rails 7.x</span>
      <span class="post-figure-step-sub">Hotwire, importmaps</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="rocket" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Rails 8.0</span>
      <span class="post-figure-step-sub">Solid trifecta</span>
    </div>
  </div>
  <figcaption class="post-figure-caption">Upgrade through each major version in sequence — never jump straight from 5 to 8.</figcaption>
</figure>

<h5 id="why-bother-upgrading">Why bother upgrading?</h5>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Security:</code> Only the latest few releases get patches. An unpatched app is a liability.</li>
  <li><code class="language-plaintext highlighter-rouge">Hiring:</code> Strong developers want to work on a modern stack, not babysit a 2018 codebase.</li>
  <li><code class="language-plaintext highlighter-rouge">Cost &amp; speed:</code> Newer Rails and Ruby are faster and cheaper to run, and unlock features like Hotwire and the Solid trifecta.</li>
</ul>

<h5 id="the-golden-rule-one-version-at-a-time">The golden rule: one version at a time</h5>

<p>The single biggest mistake is jumping from 5 straight to 8. Each major version has its own deprecations and removals; skipping them turns a series of small, debuggable changes into one giant, cascading mess. Walk the path: <code class="language-plaintext highlighter-rouge">5.2 → 6.0 → 6.1 → 7.0 → 7.1 → 7.2 → 8.0</code>.</p>

<h5 id="step-1-lock-in-a-test-baseline">Step 1: Lock in a test baseline</h5>

<p>You can’t upgrade safely without a green test suite. Run it first and note what passes — that’s your safety net for every step that follows.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle <span class="nb">exec </span>rspec   <span class="c"># or: bin/rails test</span>
</code></pre></div></div>

<p>If coverage is thin, invest here before touching anything. Tests are what tell you an upgrade broke something.</p>

<h5 id="step-2-upgrade-ruby-first">Step 2: Upgrade Ruby first</h5>

<p>Each Rails version requires a minimum Ruby. Bump Ruby on its own, get the suite green, and deploy that — so you’re only changing one thing at a time.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># .ruby-version</span>
<span class="mf">3.3</span><span class="o">.</span><span class="mi">6</span>
</code></pre></div></div>

<h5 id="step-3-bump-one-rails-version">Step 3: Bump one Rails version</h5>

<p>Point the Gemfile at the next version and update just Rails and its dependencies:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Gemfile</span>
<span class="n">gem</span> <span class="s2">"rails"</span><span class="p">,</span> <span class="s2">"~&gt; 6.1.0"</span>
</code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle update rails
</code></pre></div></div>

<p>Then run the interactive updater, which walks you through framework default and config changes:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bin/rails app:update
</code></pre></div></div>

<p>Review every diff it proposes — especially files in <code class="language-plaintext highlighter-rouge">config/initializers</code> and <code class="language-plaintext highlighter-rouge">config/environments</code>.</p>

<h5 id="step-4-adopt-new-defaults-gradually">Step 4: Adopt new defaults gradually</h5>

<p>Rails gates new behaviour behind <code class="language-plaintext highlighter-rouge">config.load_defaults</code>. After upgrading, keep the <em>old</em> defaults until the app is stable, then raise them deliberately:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># config/application.rb</span>
<span class="n">config</span><span class="p">.</span><span class="nf">load_defaults</span> <span class="mf">6.1</span>
</code></pre></div></div>

<p>The generated <code class="language-plaintext highlighter-rouge">config/initializers/new_framework_defaults_*.rb</code> file lets you flip individual settings on one at a time instead of all at once.</p>

<h5 id="step-5-audit-your-gems">Step 5: Audit your gems</h5>

<p>A blocked dependency can derail the whole project. Before each jump, check that your gems support the target version:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle outdated
</code></pre></div></div>

<p>Read the changelogs for anything major. Replace abandoned gems early — discovering a dead gem halfway through is the classic mid-upgrade trap.</p>

<h5 id="step-6-fix-deprecations-and-validate">Step 6: Fix deprecations and validate</h5>

<p>Run the suite again, fix what broke, and watch the logs for deprecation warnings — those are next version’s removals. Repeat steps 3–6 for each version until you reach 8.0, deploying after each successful jump.</p>

<h5 id="five-traps-to-avoid">Five traps to avoid</h5>

<ol>
  <li><code class="language-plaintext highlighter-rouge">Jumping multiple majors at once</code> — the fastest way to a mess.</li>
  <li><code class="language-plaintext highlighter-rouge">Upgrading with weak tests</code> — you’ll ship regressions blind.</li>
  <li><code class="language-plaintext highlighter-rouge">Skipping the dependency audit</code> — a dead gem can block you for days.</li>
  <li><code class="language-plaintext highlighter-rouge">Flipping all new defaults immediately</code> — raise <code class="language-plaintext highlighter-rouge">load_defaults</code> only once stable.</li>
  <li><code class="language-plaintext highlighter-rouge">Treating it as pure tech debt</code> — frame it as the business investment it is, and protect dedicated time for it.</li>
</ol>

<h5 id="wrapping-up">Wrapping up</h5>

<p>A Rails upgrade isn’t a heroic rewrite — it’s a disciplined loop: bump one version, run <code class="language-plaintext highlighter-rouge">app:update</code>, audit gems, fix deprecations, get green, deploy, repeat. Do that, and you’ll land on Rails 8 with its Solid Queue, Solid Cache, and Kamal goodies, on a codebase that’s secure, fast, and a pleasure to hire for.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Staying on an old Rails version feels safe, but it quietly piles up risk: missing security patches, a shrinking pool of developers who want to work on it, and features you simply can’t use. The good news is that upgrading is a well-trodden path — as long as you do it one major version at a time and lean on the tooling Rails gives you. Here’s a practical route from Rails 5/6 all the way to Rails 8.]]></summary></entry><entry><title type="html">Managing Secrets With SOPS</title><link href="https://bhupendranegi.github.io/Managing-Secrets-With-SOPS/" rel="alternate" type="text/html" title="Managing Secrets With SOPS" /><published>2025-03-10T00:00:00+00:00</published><updated>2025-03-10T00:00:00+00:00</updated><id>https://bhupendranegi.github.io/Managing-Secrets-With-SOPS</id><content type="html" xml:base="https://bhupendranegi.github.io/Managing-Secrets-With-SOPS/"><![CDATA[<hr />
<p>Secrets management is a problem in every stack, not just Rails. <a href="https://github.com/getsops/sops">SOPS</a> (Secrets OPerationS) is a framework-agnostic tool that encrypts just the <strong>values</strong> in a YAML/JSON/ENV file, supports multiple keys and cloud KMS, and produces diffs you can actually review — so you can safely commit your secrets to git. It works with anything, but since I reach for it most often on Rails, that’s the example we’ll use here. Rails’ own encrypted credentials are a fine starting point, but they have limits: one shared key, an all-or-nothing file, and noisy diffs — exactly what SOPS fixes.</p>

<figure class="post-figure">
  <div class="post-figure-flow">
    <div class="post-figure-step">
      <i data-lucide="key" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Secrets</span>
      <span class="post-figure-step-sub">Plaintext values</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="lock" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">SOPS</span>
      <span class="post-figure-step-sub">Encrypt with an age key</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="git-branch" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Git</span>
      <span class="post-figure-step-sub">Safe to commit</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="server" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Rails</span>
      <span class="post-figure-step-sub">Decrypted into ENV</span>
    </div>
  </div>
  <figcaption class="post-figure-caption">SOPS encrypts values at rest; only the decrypt key can turn them back into ENV vars at runtime.</figcaption>
</figure>

<h5 id="why-sops-over-rails-credentials">Why SOPS over Rails credentials?</h5>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Reviewable diffs:</code> Only values are encrypted, so keys and structure stay readable. A PR shows <em>which</em> secret changed, not a wall of ciphertext.</li>
  <li><code class="language-plaintext highlighter-rouge">Many keys:</code> Encrypt to several recipients at once — each developer’s key, plus a CI key. Revoke one without re-sharing a master key.</li>
  <li><code class="language-plaintext highlighter-rouge">KMS-ready:</code> Back it with AWS KMS, GCP KMS, Azure Key Vault, age, or PGP.</li>
  <li><code class="language-plaintext highlighter-rouge">Per-environment files:</code> Keep <code class="language-plaintext highlighter-rouge">staging</code> and <code class="language-plaintext highlighter-rouge">production</code> secrets in separate, independently-encrypted files.</li>
</ul>

<h5 id="install-sops-and-age">Install SOPS and age</h5>

<p>We’ll use <a href="https://github.com/FiloSottile/age">age</a> for keys — it’s simpler than PGP and perfect for a small team.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>sops age
</code></pre></div></div>

<p>Generate a key pair. The public key encrypts; the private key (kept off git) decrypts.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>age-keygen <span class="nt">-o</span> config/sops/age.key
<span class="c"># Public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8r</span>
</code></pre></div></div>

<h5 id="tell-sops-what-to-encrypt">Tell SOPS what to encrypt</h5>

<p>A <code class="language-plaintext highlighter-rouge">.sops.yaml</code> at the repo root defines creation rules — which files to encrypt and to which recipients:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># .sops.yaml</span>
<span class="na">creation_rules</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">path_regex</span><span class="pi">:</span> <span class="s">config/secrets/.*\.yml$</span>
    <span class="na">age</span><span class="pi">:</span> <span class="s">age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8r</span>
</code></pre></div></div>

<h5 id="encrypt-a-secrets-file">Encrypt a secrets file</h5>

<p>Write your secrets as plain YAML:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># config/secrets/production.yml</span>
<span class="na">DATABASE_URL</span><span class="pi">:</span> <span class="s">postgres://user:pass@db.internal/myapp</span>
<span class="na">STRIPE_SECRET_KEY</span><span class="pi">:</span> <span class="s">sk_live_abc123</span>
<span class="na">RAILS_MASTER_KEY</span><span class="pi">:</span> <span class="s">0a1b2c3d4e5f</span>
</code></pre></div></div>

<p>Then encrypt it in place. SOPS rewrites the file so every value becomes ciphertext while the keys stay legible:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sops <span class="nt">--encrypt</span> <span class="nt">--in-place</span> config/secrets/production.yml
</code></pre></div></div>

<p>The result is safe to commit — <code class="language-plaintext highlighter-rouge">DATABASE_URL</code> is still visible, but its value is now <code class="language-plaintext highlighter-rouge">ENC[AES256_GCM,data:...]</code> plus a <code class="language-plaintext highlighter-rouge">sops:</code> metadata block.</p>

<h5 id="load-secrets-into-rails">Load secrets into Rails</h5>

<p>The cleanest runtime approach is <code class="language-plaintext highlighter-rouge">sops exec-env</code>, which decrypts into the process environment and runs your command — no plaintext ever touches disk:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">SOPS_AGE_KEY_FILE</span><span class="o">=</span>config/sops/age.key <span class="se">\</span>
  sops exec-env config/secrets/production.yml <span class="s1">'bin/rails server'</span>
</code></pre></div></div>

<p>Prefer to decrypt inside the app? Read and parse it in an initializer:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># config/initializers/sops.rb</span>
<span class="n">secrets_file</span> <span class="o">=</span> <span class="no">Rails</span><span class="p">.</span><span class="nf">root</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s2">"config/secrets/</span><span class="si">#{</span><span class="no">Rails</span><span class="p">.</span><span class="nf">env</span><span class="si">}</span><span class="s2">.yml"</span><span class="p">)</span>

<span class="k">if</span> <span class="n">secrets_file</span><span class="p">.</span><span class="nf">exist?</span>
  <span class="n">decrypted</span> <span class="o">=</span> <span class="sb">`sops --decrypt </span><span class="si">#{</span><span class="n">secrets_file</span><span class="si">}</span><span class="sb">`</span>
  <span class="no">YAML</span><span class="p">.</span><span class="nf">safe_load</span><span class="p">(</span><span class="n">decrypted</span><span class="p">).</span><span class="nf">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="o">|</span> <span class="no">ENV</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">||=</span> <span class="n">value</span><span class="p">.</span><span class="nf">to_s</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Now <code class="language-plaintext highlighter-rouge">ENV["STRIPE_SECRET_KEY"]</code> is available throughout the app, just like any other environment variable.</p>

<h5 id="editing-secrets-later">Editing secrets later</h5>

<p>Never edit the encrypted file by hand. <code class="language-plaintext highlighter-rouge">sops</code> opens the decrypted version in your editor and re-encrypts on save:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sops config/secrets/production.yml
</code></pre></div></div>

<h5 id="in-cicd">In CI/CD</h5>

<p>Store the <strong>private</strong> age key as a single CI secret (e.g. <code class="language-plaintext highlighter-rouge">SOPS_AGE_KEY</code>), and let your pipeline decrypt at deploy time. One secret in your CI provider unlocks everything else — and that one key never lands in the repo.</p>

<h5 id="wrapping-up">Wrapping up</h5>

<p>SOPS turns secrets management into normal version control: encrypted files live next to your code, diffs are reviewable, and access is controlled by who holds a key. For teams that have outgrown a single shared <code class="language-plaintext highlighter-rouge">master.key</code>, it’s a clean, auditable upgrade — and it plays nicely with whatever Rails version you’re on.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Secrets management is a problem in every stack, not just Rails. SOPS (Secrets OPerationS) is a framework-agnostic tool that encrypts just the values in a YAML/JSON/ENV file, supports multiple keys and cloud KMS, and produces diffs you can actually review — so you can safely commit your secrets to git. It works with anything, but since I reach for it most often on Rails, that’s the example we’ll use here. Rails’ own encrypted credentials are a fine starting point, but they have limits: one shared key, an all-or-nothing file, and noisy diffs — exactly what SOPS fixes.]]></summary></entry><entry><title type="html">Deploying Rails With Kamal 2.0</title><link href="https://bhupendranegi.github.io/Deploying-Rails-With-Kamal-2/" rel="alternate" type="text/html" title="Deploying Rails With Kamal 2.0" /><published>2025-01-02T00:00:00+00:00</published><updated>2025-01-02T00:00:00+00:00</updated><id>https://bhupendranegi.github.io/Deploying-Rails-With-Kamal-2</id><content type="html" xml:base="https://bhupendranegi.github.io/Deploying-Rails-With-Kamal-2/"><![CDATA[<hr />
<p>Platforms like Heroku are convenient, but they get expensive fast — and you give up control. Kamal, the deploy tool that now ships with Rails, lets you deploy a containerised app to any server you can SSH into, with zero-downtime releases and automatic SSL. Version 2.0 made it dramatically simpler. Let’s ship a Rails app to a plain VPS.</p>

<figure class="post-figure">
  <div class="post-figure-flow">
    <div class="post-figure-step">
      <i data-lucide="code" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Your app</span>
      <span class="post-figure-step-sub">Rails + Dockerfile</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="package" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Image</span>
      <span class="post-figure-step-sub">kamal build</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="upload-cloud" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Registry</span>
      <span class="post-figure-step-sub">Push the image</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="server" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Servers</span>
      <span class="post-figure-step-sub">Zero-downtime swap</span>
    </div>
  </div>
  <figcaption class="post-figure-caption">Kamal builds a Docker image, pushes it to a registry, and rolls it out across your servers.</figcaption>
</figure>

<h5 id="what-is-kamal">What is Kamal?</h5>

<p>Kamal deploys web apps as Docker containers to your own servers. It SSHes in, pulls your image, boots the new container, health-checks it, and only then routes traffic to it — so users never see a blip. You own the infrastructure, but you get a Heroku-like <code class="language-plaintext highlighter-rouge">git push</code> workflow.</p>

<h5 id="whats-new-in-20">What’s new in 2.0</h5>

<ul>
  <li><code class="language-plaintext highlighter-rouge">kamal-proxy:</code> A purpose-built reverse proxy replaces Traefik. It handles zero-downtime cutovers and <strong>automatic HTTPS</strong> via Let’s Encrypt out of the box.</li>
  <li><code class="language-plaintext highlighter-rouge">Easier multi-app hosting:</code> Run several apps on one server without fighting over ports.</li>
  <li><code class="language-plaintext highlighter-rouge">First-class secrets:</code> A dedicated <code class="language-plaintext highlighter-rouge">.kamal/secrets</code> file with helpers to pull values from your environment or a password manager.</li>
</ul>

<h5 id="install-and-initialise">Install and initialise</h5>

<p>Kamal ships with Rails 8. On older apps, add it and generate the config:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem <span class="nb">install </span>kamal
kamal init
</code></pre></div></div>

<p>This creates <code class="language-plaintext highlighter-rouge">config/deploy.yml</code>, a <code class="language-plaintext highlighter-rouge">.kamal/secrets</code> file, and a <code class="language-plaintext highlighter-rouge">Dockerfile</code> (Rails 7.1+ already includes a production-ready one).</p>

<h5 id="configure-the-deploy">Configure the deploy</h5>

<p><code class="language-plaintext highlighter-rouge">config/deploy.yml</code> is the heart of it — name the service, the image, your servers, and the registry:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">service</span><span class="pi">:</span> <span class="s">myapp</span>
<span class="na">image</span><span class="pi">:</span> <span class="s">yourname/myapp</span>

<span class="na">servers</span><span class="pi">:</span>
  <span class="na">web</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">192.168.0.1</span>

<span class="na">proxy</span><span class="pi">:</span>
  <span class="na">ssl</span><span class="pi">:</span> <span class="kc">true</span>
  <span class="na">host</span><span class="pi">:</span> <span class="s">myapp.com</span>

<span class="na">registry</span><span class="pi">:</span>
  <span class="na">username</span><span class="pi">:</span> <span class="s">yourname</span>
  <span class="na">password</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">KAMAL_REGISTRY_PASSWORD</span>

<span class="na">env</span><span class="pi">:</span>
  <span class="na">secret</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">RAILS_MASTER_KEY</span>
  <span class="na">clear</span><span class="pi">:</span>
    <span class="na">RAILS_ENV</span><span class="pi">:</span> <span class="s">production</span>
</code></pre></div></div>

<p>Setting <code class="language-plaintext highlighter-rouge">proxy.ssl: true</code> with a <code class="language-plaintext highlighter-rouge">host</code> is all it takes for kamal-proxy to provision and renew a Let’s Encrypt certificate.</p>

<h5 id="manage-secrets">Manage secrets</h5>

<p>In 2.0, secrets live in <code class="language-plaintext highlighter-rouge">.kamal/secrets</code> (never commit the resolved values). You can read them straight from the environment or a tool like 1Password:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># .kamal/secrets</span>
<span class="nv">KAMAL_REGISTRY_PASSWORD</span><span class="o">=</span><span class="nv">$KAMAL_REGISTRY_PASSWORD</span>
<span class="nv">RAILS_MASTER_KEY</span><span class="o">=</span><span class="si">$(</span><span class="nb">cat </span>config/master.key<span class="si">)</span>
</code></pre></div></div>

<h5 id="your-first-deploy">Your first deploy</h5>

<p>Provision the servers (installs Docker, boots the proxy) once, then deploy:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kamal setup
</code></pre></div></div>

<p>After that, every release is a single command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kamal deploy
</code></pre></div></div>

<p>Kamal builds the image, pushes it, pulls it on each server, runs your <code class="language-plaintext highlighter-rouge">bin/docker-entrypoint</code> (migrations included), health-checks the new container, and swaps traffic over with no downtime.</p>

<h5 id="day-to-day-commands">Day-to-day commands</h5>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kamal app logs <span class="nt">-f</span>        <span class="c"># tail logs</span>
kamal console            <span class="c"># rails console on the server</span>
kamal rollback           <span class="c"># revert to the previous release</span>
kamal app <span class="nb">exec</span> <span class="s2">"bin/rails db:migrate"</span>
</code></pre></div></div>

<h5 id="wrapping-up">Wrapping up</h5>

<p>Kamal 2.0 hits a sweet spot: the simplicity of a PaaS with the cost and control of your own hardware. Once <code class="language-plaintext highlighter-rouge">config/deploy.yml</code> is set up, shipping is just <code class="language-plaintext highlighter-rouge">kamal deploy</code> — and automatic SSL plus zero-downtime cutovers mean you spend time building features, not babysitting servers.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Platforms like Heroku are convenient, but they get expensive fast — and you give up control. Kamal, the deploy tool that now ships with Rails, lets you deploy a containerised app to any server you can SSH into, with zero-downtime releases and automatic SSL. Version 2.0 made it dramatically simpler. Let’s ship a Rails app to a plain VPS.]]></summary></entry><entry><title type="html">Reactive Rails With Hotwire</title><link href="https://bhupendranegi.github.io/Reactive-Rails-With-Hotwire/" rel="alternate" type="text/html" title="Reactive Rails With Hotwire" /><published>2024-07-10T00:00:00+00:00</published><updated>2024-07-10T00:00:00+00:00</updated><id>https://bhupendranegi.github.io/Reactive-Rails-With-Hotwire</id><content type="html" xml:base="https://bhupendranegi.github.io/Reactive-Rails-With-Hotwire/"><![CDATA[<hr />
<p>You don’t always need a single-page app to build a snappy, modern UI. Hotwire — the default front-end stack that ships with Rails — lets you build reactive interfaces by sending <strong>HTML over the wire</strong> instead of JSON, keeping your logic on the server where Rails is strongest. It’s made up of three pieces: Turbo, Stimulus, and Turbo Native.</p>

<figure class="post-figure">
  <div class="post-figure-flow">
    <div class="post-figure-step">
      <i data-lucide="mouse-pointer-click" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">User action</span>
      <span class="post-figure-step-sub">Click · submit · visit</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="server" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Rails</span>
      <span class="post-figure-step-sub">Renders an HTML partial</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="zap" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Turbo</span>
      <span class="post-figure-step-sub">Streams HTML to the page</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="monitor-check" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">DOM</span>
      <span class="post-figure-step-sub">Updated in place</span>
    </div>
  </div>
  <figcaption class="post-figure-caption">Hotwire keeps rendering on the server and ships HTML — not JSON — to update the page.</figcaption>
</figure>

<h5 id="what-is-hotwire">What is Hotwire?</h5>

<p>Hotwire (HTML Over The Wire) is an approach to building web apps without writing much JavaScript. Rather than serialising state to JSON and rebuilding the DOM on the client, you send the markup the browser actually needs and let the framework swap it in.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Turbo:</code> Speeds up navigation and updates the page in fragments — no custom JS required.</li>
  <li><code class="language-plaintext highlighter-rouge">Stimulus:</code> A small JavaScript framework for the bits that genuinely need behaviour.</li>
  <li><code class="language-plaintext highlighter-rouge">Turbo Native:</code> Wraps your web app in a hybrid iOS/Android shell.</li>
</ul>

<h5 id="turbo-drive">Turbo Drive</h5>

<p>Turbo Drive intercepts link clicks and form submissions, fetches the new page in the background, and swaps the <code class="language-plaintext highlighter-rouge">&lt;body&gt;</code> — giving you SPA-like speed with zero code. It’s on by default. When you need to opt a link out, just tell it:</p>

<div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"Download report"</span><span class="p">,</span> <span class="n">report_path</span><span class="p">,</span> <span class="ss">data: </span><span class="p">{</span> <span class="ss">turbo: </span><span class="kp">false</span> <span class="p">}</span> <span class="cp">%&gt;</span>
</code></pre></div></div>

<h5 id="turbo-frames">Turbo Frames</h5>

<p>A Turbo Frame is a slice of the page that updates independently. Wrap content in a <code class="language-plaintext highlighter-rouge">turbo_frame_tag</code>, and any link or form inside it replaces only that frame on response.</p>

<div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;%# app/views/posts/show.html.erb %&gt;</span>
<span class="cp">&lt;%=</span> <span class="n">turbo_frame_tag</span> <span class="s2">"post_</span><span class="si">#{</span><span class="vi">@post</span><span class="p">.</span><span class="nf">id</span><span class="si">}</span><span class="s2">"</span> <span class="k">do</span> <span class="cp">%&gt;</span>
  <span class="nt">&lt;h2&gt;</span><span class="cp">&lt;%=</span> <span class="vi">@post</span><span class="p">.</span><span class="nf">title</span> <span class="cp">%&gt;</span><span class="nt">&lt;/h2&gt;</span>
  <span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"Edit"</span><span class="p">,</span> <span class="n">edit_post_path</span><span class="p">(</span><span class="vi">@post</span><span class="p">)</span> <span class="cp">%&gt;</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
</code></pre></div></div>

<p>When the user clicks <strong>Edit</strong>, Rails renders the edit view; Turbo extracts the matching frame and swaps it in. The rest of the page never reloads.</p>

<h5 id="turbo-streams">Turbo Streams</h5>

<p>Turbo Streams let the server push fine-grained changes — <code class="language-plaintext highlighter-rouge">append</code>, <code class="language-plaintext highlighter-rouge">prepend</code>, <code class="language-plaintext highlighter-rouge">replace</code>, <code class="language-plaintext highlighter-rouge">update</code>, <code class="language-plaintext highlighter-rouge">remove</code> — to specific DOM targets. They’re perfect for things like adding a comment without a full reload.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># app/controllers/comments_controller.rb</span>
<span class="k">def</span> <span class="nf">create</span>
  <span class="vi">@comment</span> <span class="o">=</span> <span class="vi">@post</span><span class="p">.</span><span class="nf">comments</span><span class="p">.</span><span class="nf">create!</span><span class="p">(</span><span class="n">comment_params</span><span class="p">)</span>

  <span class="n">respond_to</span> <span class="k">do</span> <span class="o">|</span><span class="nb">format</span><span class="o">|</span>
    <span class="nb">format</span><span class="p">.</span><span class="nf">turbo_stream</span>
    <span class="nb">format</span><span class="p">.</span><span class="nf">html</span> <span class="p">{</span> <span class="n">redirect_to</span> <span class="vi">@post</span> <span class="p">}</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;%# app/views/comments/create.turbo_stream.erb %&gt;</span>
<span class="cp">&lt;%=</span> <span class="n">turbo_stream</span><span class="p">.</span><span class="nf">append</span> <span class="s2">"comments"</span><span class="p">,</span> <span class="vi">@comment</span> <span class="cp">%&gt;</span>
<span class="cp">&lt;%=</span> <span class="n">turbo_stream</span><span class="p">.</span><span class="nf">update</span> <span class="s2">"comments_count"</span><span class="p">,</span> <span class="vi">@post</span><span class="p">.</span><span class="nf">comments</span><span class="p">.</span><span class="nf">count</span> <span class="cp">%&gt;</span>
</code></pre></div></div>

<p>Pair this with <code class="language-plaintext highlighter-rouge">broadcasts_to</code> in your model and Action Cable, and the same stream reaches every connected user in real time:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Comment</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
  <span class="n">belongs_to</span> <span class="ss">:post</span>
  <span class="n">broadcasts_to</span> <span class="ss">:post</span>
<span class="k">end</span>
</code></pre></div></div>

<h5 id="stimulus-for-the-rest">Stimulus for the rest</h5>

<p>Some interactions are purely client-side — a dropdown, a copy-to-clipboard button, a character counter. That’s Stimulus’s job. It connects plain HTML to small, well-named controllers.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// app/javascript/controllers/clipboard_controller.js</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">Controller</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">@hotwired/stimulus</span><span class="dl">"</span>

<span class="k">export</span> <span class="k">default</span> <span class="kd">class</span> <span class="nc">extends</span> <span class="nx">Controller</span> <span class="p">{</span>
  <span class="kd">static</span> <span class="nx">targets</span> <span class="o">=</span> <span class="p">[</span><span class="dl">"</span><span class="s2">source</span><span class="dl">"</span><span class="p">]</span>

  <span class="nf">copy</span><span class="p">()</span> <span class="p">{</span>
    <span class="nb">navigator</span><span class="p">.</span><span class="nx">clipboard</span><span class="p">.</span><span class="nf">writeText</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">sourceTarget</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">data-controller=</span><span class="s">"clipboard"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;input</span> <span class="na">data-clipboard-target=</span><span class="s">"source"</span> <span class="na">value=</span><span class="s">"https://example.com/abc"</span> <span class="na">readonly</span><span class="nt">&gt;</span>
  <span class="nt">&lt;button</span> <span class="na">data-action=</span><span class="s">"clipboard#copy"</span><span class="nt">&gt;</span>Copy<span class="nt">&lt;/button&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<h5 id="when-should-you-reach-for-hotwire">When should you reach for Hotwire?</h5>

<p>Hotwire shines for CRUD-heavy, business-logic apps — dashboards, admin panels, marketplaces — where most of the work is on the server anyway. You keep one language, one set of templates, and skip the API layer entirely. If you’re building something with heavy client-side state (a drawing tool, a game), a dedicated front-end framework still makes sense. For everything else, Hotwire gets you a fast, reactive UI with a fraction of the code.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[You don’t always need a single-page app to build a snappy, modern UI. Hotwire — the default front-end stack that ships with Rails — lets you build reactive interfaces by sending HTML over the wire instead of JSON, keeping your logic on the server where Rails is strongest. It’s made up of three pieces: Turbo, Stimulus, and Turbo Native.]]></summary></entry><entry><title type="html">Solid Queue With Rails</title><link href="https://bhupendranegi.github.io/Solid-Queue-With-Rails/" rel="alternate" type="text/html" title="Solid Queue With Rails" /><published>2024-02-02T00:00:00+00:00</published><updated>2024-02-02T00:00:00+00:00</updated><id>https://bhupendranegi.github.io/Solid-Queue-With-Rails</id><content type="html" xml:base="https://bhupendranegi.github.io/Solid-Queue-With-Rails/"><![CDATA[<hr />
<p>In a world where applications need to handle thousands of tasks efficiently, job queues are the unsung heroes. They help process background tasks, ensuring your application stays responsive while heavy lifting happens in the background. One such solution that stands out is Solid Queue.</p>

<figure class="post-figure">
  <div class="post-figure-flow">
    <div class="post-figure-step">
      <i data-lucide="server" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Producer</span>
      <span class="post-figure-step-sub">Rails enqueues a job</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="layers" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Queue</span>
      <span class="post-figure-step-sub">Durable, DB-backed store</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="cpu" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Consumer</span>
      <span class="post-figure-step-sub">Workers run jobs concurrently</span>
    </div>
  </div>
  <figcaption class="post-figure-caption">Solid Queue's producer–consumer model keeps the web request fast while workers do the heavy lifting.</figcaption>
</figure>

<h5 id="why-solid-queue-is-fast">Why Solid Queue is Fast</h5>

<p><code class="language-plaintext highlighter-rouge">1. Lightweight and Minimal Overhead:</code> Solid Queue avoids unnecessary complexity by focusing on core job queuing. Its lightweight design ensures it has minimal impact on your system resources. Unlike some heavy queuing systems, Solid Queue doesn’t add extra bloat to your stack.</p>

<p><code class="language-plaintext highlighter-rouge">2. Concurrency and Parallelism:</code> Solid Queue uses Ruby’s threading capabilities effectively. It allows multiple tasks to run concurrently, ensuring maximum CPU utilization without bottlenecks. This design makes it ideal for high-throughput applications.</p>

<p><code class="language-plaintext highlighter-rouge">3. In-Memory Queuing:</code> By default, Solid Queue operates in memory, making task enqueueing and dequeueing extremely fast. For production use, it supports integration with persistent storage like Redis or a database, ensuring job durability.</p>

<p><code class="language-plaintext highlighter-rouge">4. Customizable Workers:</code> Solid Queue lets you define workers that can be tuned for specific tasks, enabling you to optimize resource allocation and processing efficiency.</p>

<p><code class="language-plaintext highlighter-rouge">5. Retry Mechanism:</code> `Built-in support for retrying failed jobs ensures that transient errors don’t disrupt task processing. This reliability feature enhances application stability.</p>

<h5 id="how-solid-queue-works">How Solid Queue Works</h5>

<p>Solid Queue operates on a producer-consumer model: This separation ensures that heavy tasks don’t block the main application, keeping your users happy with fast responses.</p>

<p><code class="language-plaintext highlighter-rouge">Producer:</code> Rails application enqueues tasks (jobs) into the queue.</p>

<p><code class="language-plaintext highlighter-rouge">Queue:</code> Tasks are stored in memory or a persistent storage backend.</p>

<p><code class="language-plaintext highlighter-rouge">Consumer:</code> Worker threads fetch and execute tasks from the queue.</p>

<h5 id="implementation">Implementation</h5>

<h5 id="install-the-gem">Install the Gem</h5>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gem</span> <span class="s1">'solid_queue'</span>
</code></pre></div></div>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">bundle</span> <span class="n">install</span>
</code></pre></div></div>

<h5 id="configure-solid-queue">Configure Solid Queue</h5>

<p>Create an initializer file to set up Solid Queue. For example, config/initializers/solid_queue.rb:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">SolidQueue</span><span class="p">.</span><span class="nf">configure</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
  <span class="n">config</span><span class="p">.</span><span class="nf">worker_count</span> <span class="o">=</span> <span class="mi">5</span>  <span class="c1"># Number of concurrent workers</span>
  <span class="n">config</span><span class="p">.</span><span class="nf">logger</span> <span class="o">=</span> <span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span>  <span class="c1"># Use Rails' logger for job logs</span>
  <span class="n">config</span><span class="p">.</span><span class="nf">retry_attempts</span> <span class="o">=</span> <span class="mi">3</span>  <span class="c1"># Retry failed jobs 3 times</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Here, you configure the number of workers (threads), logging, and retry attempts. You can add more configurations based on your application’s needs.</p>

<h5 id="define-your-jobs">Define Your Jobs</h5>

<p>Create a directory for your jobs (e.g., app/jobs). Then, define your job classes. Each job must implement a perform method:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">MyBackgroundJob</span>
  <span class="k">def</span> <span class="nf">perform</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    <span class="c1"># Simulate a background task</span>
    <span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span> <span class="s2">"Processing data: </span><span class="si">#{</span><span class="n">data</span><span class="si">}</span><span class="s2">"</span>
    <span class="c1"># Example task: Send an email or process a file</span>
    <span class="no">UserMailer</span><span class="p">.</span><span class="nf">welcome_email</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="ss">:user_id</span><span class="p">]).</span><span class="nf">deliver_now</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>You can define as many job classes as needed for different tasks. Each job encapsulates the logic for a specific background task.</p>

<p>Adding Job with Complex Logic</p>

<p>For tasks with multiple steps, break down the logic into helper methods:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">ComplexJob</span>
  <span class="k">def</span> <span class="nf">perform</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    <span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span> <span class="s2">"Starting complex job for data: </span><span class="si">#{</span><span class="n">data</span><span class="si">}</span><span class="s2">"</span>
    <span class="n">step_one</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    <span class="n">step_two</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    <span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span> <span class="s2">"Completed complex job for data: </span><span class="si">#{</span><span class="n">data</span><span class="si">}</span><span class="s2">"</span>
  <span class="k">end</span>

  <span class="kp">private</span>

  <span class="k">def</span> <span class="nf">step_one</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    <span class="c1"># Perform the first step</span>
    <span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span> <span class="s2">"Step one with: </span><span class="si">#{</span><span class="n">data</span><span class="p">[</span><span class="ss">:step_one_info</span><span class="p">]</span><span class="si">}</span><span class="s2">"</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">step_two</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    <span class="c1"># Perform the second step</span>
    <span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span> <span class="s2">"Step two with: </span><span class="si">#{</span><span class="n">data</span><span class="p">[</span><span class="ss">:step_two_info</span><span class="p">]</span><span class="si">}</span><span class="s2">"</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>This structure ensures that your code remains readable and maintainable.</p>

<h5 id="enqueue-jobs">Enqueue Jobs</h5>

<p>You can enqueue jobs from controllers, models, or services. Here’s an example from a controller:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
  <span class="k">def</span> <span class="nf">create</span>
    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">user_params</span><span class="p">)</span>

    <span class="k">if</span> <span class="vi">@user</span><span class="p">.</span><span class="nf">save</span>
      <span class="c1"># Enqueue a background job</span>
      <span class="no">SolidQueue</span><span class="p">.</span><span class="nf">enqueue</span><span class="p">(</span><span class="no">MyBackgroundJob</span><span class="p">.</span><span class="nf">new</span><span class="p">,</span> <span class="ss">data: </span><span class="p">{</span> <span class="ss">user_id: </span><span class="vi">@user</span><span class="p">.</span><span class="nf">id</span> <span class="p">})</span>

      <span class="c1"># Enqueue a complex job</span>
      <span class="no">SolidQueue</span><span class="p">.</span><span class="nf">enqueue</span><span class="p">(</span><span class="no">ComplexJob</span><span class="p">.</span><span class="nf">new</span><span class="p">,</span> <span class="ss">data: </span><span class="p">{</span> <span class="ss">step_one_info: </span><span class="s2">"info1"</span><span class="p">,</span> <span class="ss">step_two_info: </span><span class="s2">"info2"</span> <span class="p">})</span>

      <span class="n">redirect_to</span> <span class="vi">@user</span><span class="p">,</span> <span class="ss">notice: </span><span class="s1">'User was successfully created.'</span>
    <span class="k">else</span>
      <span class="n">render</span> <span class="ss">:new</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="kp">private</span>

  <span class="k">def</span> <span class="nf">user_params</span>
    <span class="n">params</span><span class="p">.</span><span class="nf">require</span><span class="p">(</span><span class="ss">:user</span><span class="p">).</span><span class="nf">permit</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:password</span><span class="p">,</span> <span class="ss">:password_confirmation</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Here, both a simple and a complex job are triggered after a user is successfully created.</p>

<h5 id="start-the-workers">Start the Workers</h5>

<p>Solid Queue includes a worker manager to process jobs. Start the workers using the Rails runner:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">rails</span> <span class="n">runner</span> <span class="s2">"SolidQueue.start"</span>
</code></pre></div></div>

<p>Example Worker Output
When a job runs, you might see logs like this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>Worker-1] Processing job: MyBackgroundJob with data: <span class="o">{</span>:user_id<span class="o">=&gt;</span>42<span class="o">}</span>
<span class="o">[</span>Worker-2] Processing job: ComplexJob with data: <span class="o">{</span>:step_one_info<span class="o">=&gt;</span><span class="s2">"info1"</span>, :step_two_info<span class="o">=&gt;</span><span class="s2">"info2"</span><span class="o">}</span>
<span class="o">[</span>Worker-1] Completed job: MyBackgroundJob
<span class="o">[</span>Worker-2] Completed job: ComplexJob
</code></pre></div></div>

<p>For development, you can also start the workers manually. In production, integrate it with process managers like foreman, systemd, or Docker.</p>

<h5 id="monitor-your-queue">Monitor Your Queue</h5>

<p>Monitor your jobs to ensure smooth operation. You can log queue length and worker utilization or use monitoring tools for deeper insights. For example:</p>

<p>Rails.logger.info “Current queue length: #{SolidQueue.queue_length}”</p>

<p>Implementing a Monitoring Job</p>

<p>You can create a job to monitor and alert on queue health:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">QueueMonitoringJob</span>
  <span class="k">def</span> <span class="nf">perform</span>
    <span class="n">length</span> <span class="o">=</span> <span class="no">SolidQueue</span><span class="p">.</span><span class="nf">queue_length</span>
    <span class="k">if</span> <span class="n">length</span> <span class="o">&gt;</span> <span class="mi">10</span>
      <span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">warn</span> <span class="s2">"High queue length: </span><span class="si">#{</span><span class="n">length</span><span class="si">}</span><span class="s2">"</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Schedule this job to run periodically using a cron scheduler like whenever or a similar tool.</p>

<h5 id="handling-failures">Handling Failures</h5>

<p>Solid Queue provides robust mechanisms to handle job failures. Here’s how you can leverage them:</p>

<h5 id="retry-logic">Retry Logic</h5>

<p>Solid Queue automatically retries failed jobs based on the retry_attempts configuration. You can also implement custom retry logic in your job class:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">ResilientJob</span>
  <span class="k">def</span> <span class="nf">perform</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    <span class="k">begin</span>
      <span class="c1"># Perform a task that might fail</span>
      <span class="n">process_data</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    <span class="k">rescue</span> <span class="no">StandardError</span> <span class="o">=&gt;</span> <span class="n">e</span>
      <span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">error</span> <span class="s2">"Job failed: </span><span class="si">#{</span><span class="n">e</span><span class="p">.</span><span class="nf">message</span><span class="si">}</span><span class="s2">"</span>
      <span class="k">raise</span> <span class="n">e</span>  <span class="c1"># Re-raise to trigger a retry</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="kp">private</span>

  <span class="k">def</span> <span class="nf">process_data</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    <span class="c1"># Example: Make an API call</span>
    <span class="no">RestClient</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="ss">:url</span><span class="p">])</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h5 id="dead-letter-queue-dlq">Dead Letter Queue (DLQ)</h5>

<p>For jobs that fail even after retries, you can implement a dead letter queue to store them for further inspection.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">SolidQueue</span><span class="p">.</span><span class="nf">configure</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
  <span class="n">config</span><span class="p">.</span><span class="nf">dead_letter_handler</span> <span class="o">=</span> <span class="o">-&gt;</span><span class="p">(</span><span class="n">job</span><span class="p">,</span> <span class="n">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">error</span> <span class="s2">"Job permanently failed: </span><span class="si">#{</span><span class="n">job</span><span class="p">.</span><span class="nf">inspect</span><span class="si">}</span><span class="s2">, Error: </span><span class="si">#{</span><span class="n">error</span><span class="p">.</span><span class="nf">message</span><span class="si">}</span><span class="s2">"</span>
    <span class="no">FailedJob</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="ss">job_data: </span><span class="n">job</span><span class="p">,</span> <span class="ss">error_message: </span><span class="n">error</span><span class="p">.</span><span class="nf">message</span><span class="p">)</span>
  <span class="p">}</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This ensures no job is lost, even in the event of persistent failures.</p>

<p>Inspecting Failed Jobs</p>

<p>You can build a simple admin interface to inspect failed jobs:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">FailedJobsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
  <span class="k">def</span> <span class="nf">index</span>
    <span class="vi">@failed_jobs</span> <span class="o">=</span> <span class="no">FailedJob</span><span class="p">.</span><span class="nf">all</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">retry</span>
    <span class="n">failed_job</span> <span class="o">=</span> <span class="no">FailedJob</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:id</span><span class="p">])</span>
    <span class="no">SolidQueue</span><span class="p">.</span><span class="nf">enqueue</span><span class="p">(</span><span class="nb">eval</span><span class="p">(</span><span class="n">failed_job</span><span class="p">.</span><span class="nf">job_data</span><span class="p">))</span>
    <span class="n">failed_job</span><span class="p">.</span><span class="nf">destroy</span>
    <span class="n">redirect_to</span> <span class="n">failed_jobs_path</span><span class="p">,</span> <span class="ss">notice: </span><span class="s2">"Job retried successfully."</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h5 id="best-practices-with-solid-queue">Best Practices with Solid Queue</h5>
<p><code class="language-plaintext highlighter-rouge">Use Idempotent Jobs:</code> Ensure that your jobs can run multiple times without adverse effects. For example, check for the existence of a record before creating it.</p>

<p><code class="language-plaintext highlighter-rouge">Prioritize and Categorize Jobs:</code> Use job priorities or categories to ensure critical tasks are executed first while others are queued.</p>

<p><code class="language-plaintext highlighter-rouge">Monitor Performance:</code> Keep an eye on job metrics like execution time, retries, and failures. These insights will help you optimize job efficiency.</p>

<p><code class="language-plaintext highlighter-rouge">Scale Wisely:</code> Adjust worker counts dynamically based on load using tools like Kubernetes for autoscaling or dedicated process managers.</p>

<p><code class="language-plaintext highlighter-rouge">Handle Failures Gracefully:</code> Use retry and dead letter queue mechanisms effectively to recover from transient errors while identifying systemic issues.</p>

<p><code class="language-plaintext highlighter-rouge">Log Strategically:</code> Avoid excessive logging, especially for high-frequency tasks, to prevent log bloat while still capturing essential job execution details.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[In a world where applications need to handle thousands of tasks efficiently, job queues are the unsung heroes. They help process background tasks, ensuring your application stays responsive while heavy lifting happens in the background. One such solution that stands out is Solid Queue.]]></summary></entry><entry><title type="html">Effortless Ruby ETL</title><link href="https://bhupendranegi.github.io/Effortless-Ruby-ETL/" rel="alternate" type="text/html" title="Effortless Ruby ETL" /><published>2023-11-11T00:00:00+00:00</published><updated>2023-11-11T00:00:00+00:00</updated><id>https://bhupendranegi.github.io/Effortless-Ruby-ETL</id><content type="html" xml:base="https://bhupendranegi.github.io/Effortless-Ruby-ETL/"><![CDATA[<hr />
<p>Have you ever wondered how raw data gets transformed into something meaningful and useful? Whether you’re migrating a database, cleaning up messy data, or synchronizing information between systems, you’ve likely been doing ETL (Extract-Transform-Load) without even realizing it!</p>

<figure class="post-figure">
  <div class="post-figure-flow">
    <div class="post-figure-step">
      <i data-lucide="download" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Extract</span>
      <span class="post-figure-step-sub">CSV · database · API</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="wand-2" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Transform</span>
      <span class="post-figure-step-sub">Clean · filter · reshape</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="database" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Load</span>
      <span class="post-figure-step-sub">DB · report · dashboard</span>
    </div>
  </div>
  <figcaption class="post-figure-caption">Every ETL job is the same three moves — pull it in, polish it, put it somewhere useful.</figcaption>
</figure>

<h5 id="what-is-etl">What is ETL?</h5>
<ol>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Extract:</code> The first step is like being a data detective—you gather data from various sources. Whether it’s a CSV file, a database, or even an API, your mission is to pull in all the information you need.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Transform:</code> Next, the fun part—transforming the data! This is where you clean, filter, and reformat your data into a shiny, polished version. It’s like giving your data a makeover so it’s ready for action.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Load:</code> Finally, you place the transformed data into its final destination. Whether it’s a database, a report, or a dashboard, this is where your hard work pays off, and your data is ready to be used</p>
  </li>
</ol>

<h5 id="why-ruby">Why Ruby?</h5>

<p>Ruby is a fantastic language for building ETL processes because it’s simple, powerful, and flexible. And with the Kiba gem, you can create robust ETL pipelines with ease. Let’s walk through a basic example of how to implement ETL using Kiba.</p>

<h5 id="setting-up-kiba">Setting Up Kiba</h5>
<hr />

<p>First, you’ll need to install the Kiba gem. Add it to your Gemfile:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gem</span> <span class="s1">'kiba'</span>
</code></pre></div></div>

<p>Then, run bundle install to install the gem.</p>

<h5 id="building-your-etl-pipeline">Building Your ETL Pipeline</h5>

<p><code class="language-plaintext highlighter-rouge">1. Extract the Data</code></p>

<p>We’ll start by extracting data from a CSV file. Kiba makes defining a source for your data straightforward:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s1">'kiba'</span>
<span class="nb">require</span> <span class="s1">'csv'</span>

<span class="k">module</span> <span class="nn">ETLJob</span>
  <span class="kp">extend</span> <span class="no">Kiba</span><span class="o">::</span><span class="no">DSLExtensions</span>
  <span class="n">source</span> <span class="no">Kiba</span><span class="o">::</span><span class="no">Common</span><span class="o">::</span><span class="no">Sources</span><span class="o">::</span><span class="no">CSV</span><span class="p">,</span> <span class="ss">filename: </span><span class="s1">'users.csv'</span><span class="p">,</span> <span class="ss">csv_options: </span><span class="p">{</span> <span class="ss">headers: </span><span class="kp">true</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">2. Transform the Data</code></p>

<p>Next, we’ll transform the data. We’ll clean up names, convert emails to lowercase, and ensure ages are integers. Here’s how:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">module</span> <span class="nn">ETLJob</span>
  <span class="n">transform</span> <span class="k">do</span> <span class="o">|</span><span class="n">row</span><span class="o">|</span>
    <span class="n">row</span><span class="p">[</span><span class="s1">'Name'</span><span class="p">]</span> <span class="o">=</span> <span class="n">row</span><span class="p">[</span><span class="s1">'Name'</span><span class="p">].</span><span class="nf">strip</span>
    <span class="n">row</span><span class="p">[</span><span class="s1">'Age'</span><span class="p">]</span> <span class="o">=</span> <span class="n">row</span><span class="p">[</span><span class="s1">'Age'</span><span class="p">].</span><span class="nf">to_i</span>
    <span class="n">row</span><span class="p">[</span><span class="s1">'Email'</span><span class="p">]</span> <span class="o">=</span> <span class="n">row</span><span class="p">[</span><span class="s1">'Email'</span><span class="p">].</span><span class="nf">downcase</span>
    <span class="n">row</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">3. Load the Data</code></p>

<p>Finally, we’ll load the transformed data. This integrates the ETL process into your Rails application, making data handling smooth and consistent:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># app/models/user.rb</span>
<span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
<span class="k">end</span>

<span class="c1"># etl_job.rb</span>
<span class="k">module</span> <span class="nn">ETLJob</span>
  <span class="n">destination</span> <span class="no">Kiba</span><span class="o">::</span><span class="no">Common</span><span class="o">::</span><span class="no">Destinations</span><span class="o">::</span><span class="no">Lambda</span><span class="p">,</span> <span class="o">-&gt;</span><span class="p">(</span><span class="n">row</span><span class="p">)</span> <span class="p">{</span>
    <span class="no">User</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span>
      <span class="ss">name: </span><span class="n">row</span><span class="p">[</span><span class="s1">'Name'</span><span class="p">],</span>
      <span class="ss">age: </span><span class="n">row</span><span class="p">[</span><span class="s1">'Age'</span><span class="p">],</span>
      <span class="ss">email: </span><span class="n">row</span><span class="p">[</span><span class="s1">'Email'</span><span class="p">]</span>
    <span class="p">)</span>
  <span class="p">}</span>
<span class="k">end</span>

</code></pre></div></div>

<h5 id="running-the-etl-job">Running the ETL Job</h5>

<p>Now that you’ve defined the source, transformation, and destination, it’s time to run the ETL job. Here’s how you can do it:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Kiba</span><span class="p">.</span><span class="nf">run</span><span class="p">(</span><span class="no">ETLJob</span><span class="p">)</span>
</code></pre></div></div>

<p>Once the job runs successfully, your data will be extracted from the CSV file, transformed to ensure consistency, and loaded into the SQLite database—all with just a few lines of Ruby code!</p>

<h5 id="what-we-learnt">What we learnt?</h5>
<p>ETL is a powerful process that turns raw data into valuable insights, and with Ruby and the Kiba gem, you can create your own ETL pipelines with ease. Whether you’re working on a small project or handling large-scale data processing, Ruby gives you the tools to get the job done efficiently.</p>

<p>So the next time you’re working with data in Ruby, remember: with Kiba, you’re just a few steps away from turning your data into gold!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Have you ever wondered how raw data gets transformed into something meaningful and useful? Whether you’re migrating a database, cleaning up messy data, or synchronizing information between systems, you’ve likely been doing ETL (Extract-Transform-Load) without even realizing it!]]></summary></entry><entry><title type="html">Fancy Emails With MJML</title><link href="https://bhupendranegi.github.io/Fancy-Emails-With-MJML/" rel="alternate" type="text/html" title="Fancy Emails With MJML" /><published>2023-04-24T00:00:00+00:00</published><updated>2023-04-24T00:00:00+00:00</updated><id>https://bhupendranegi.github.io/Fancy-Emails-With-MJML</id><content type="html" xml:base="https://bhupendranegi.github.io/Fancy-Emails-With-MJML/"><![CDATA[<hr />
<p>In today’s fast-paced digital world, email marketing has become a vital part of business communication. Whether it’s a promotional campaign, a newsletter, or a product update, having an attractive and responsive email can make a big difference. But creating fancy emails that look good across different devices and email clients can be a headache. This is where MJML comes to the rescue.</p>

<figure class="post-figure">
  <div class="post-figure-flow">
    <div class="post-figure-step">
      <i data-lucide="code" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">MJML</span>
      <span class="post-figure-step-sub">Clean, semantic markup</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="cog" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Compiler</span>
      <span class="post-figure-step-sub">mjml → html</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="mail" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Responsive email</span>
      <span class="post-figure-step-sub">Looks right everywhere</span>
    </div>
  </div>
  <figcaption class="post-figure-caption">Write simple MJML; the compiler emits the bulletproof, table-based HTML email clients expect.</figcaption>
</figure>

<h5 id="what-is-mjml">What is MJML?</h5>
<p>MJML (Mailjet Markup Language) is an open-source framework designed to help developers create responsive and visually appealing emails. It’s a powerful tool that makes crafting emails simpler, faster, and more efficient. Instead of dealing with messy HTML and complicated CSS for different email clients, MJML allows you to focus on content while the framework takes care of the layout and responsiveness.</p>

<h5 id="why-do-you-need-mjml">Why Do You Need MJML?</h5>
<p><code class="language-plaintext highlighter-rouge">Responsive Design Made Easy:</code> MJML’s key strength is its ability to create emails that automatically adapt to various screen sizes. It ensures that your emails look great on both desktops and mobile devices without the need for complicated media queries.</p>

<p><code class="language-plaintext highlighter-rouge">Cross-Client Compatibility:</code> Emails often render differently across email clients (Gmail, Outlook, etc.). MJML’s framework handles the inconsistencies across these platforms, ensuring a consistent look.</p>

<p><code class="language-plaintext highlighter-rouge">Simplified Markup:</code> Writing email HTML can be complex, especially when you’re trying to make the design responsive. MJML simplifies this by using an easy-to-read syntax, so you don’t have to worry about writing lengthy CSS or intricate HTML tags.</p>

<p><code class="language-plaintext highlighter-rouge">Faster Development:</code> MJML speeds up the design process because it abstracts away the repetitive and challenging parts of coding for emails. It generates clean, optimized HTML that’s ready to be sent.</p>

<p><code class="language-plaintext highlighter-rouge">Open Source:</code> MJML is free to use, and it’s continuously updated by a community of developers.</p>

<h5 id="how-does-mjml-work">How Does MJML Work?</h5>
<p>MJML works by letting you write in a custom markup language that’s easier to understand than traditional HTML. You then use the MJML engine (available as an online tool, Node.js library, or even through plugins for different code editors) to compile it into responsive HTML code ready for emails.</p>

<p>Let’s break down the structure a little. MJML has tags like  <code class="language-plaintext highlighter-rouge">&lt;mj-section&gt;, &lt;mj-column&gt;, &lt;mj-text&gt;,</code> and <code class="language-plaintext highlighter-rouge">&lt;mj-button&gt;</code>  to create sections, columns, text blocks, and buttons. You don’t need to worry about complex CSS to make these elements look good on different devices.</p>

<h5 id="mjml-example">MJML Example:</h5>
<p>Let’s take a look at a professional, well-designed email using MJML, perfect for a Product Launch or Special Offer campaign. This example will demonstrate how to create an engaging, visually appealing email with clear branding, a call-to-action, and mobile responsiveness.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;mjml&gt;</span>
  <span class="nt">&lt;mj-body</span> <span class="na">background-color=</span><span class="s">"#f4f7fa"</span><span class="nt">&gt;</span>
    <span class="c">&lt;!-- Header Section --&gt;</span>
    <span class="nt">&lt;mj-section</span> <span class="na">background-color=</span><span class="s">"#ffffff"</span> <span class="na">padding=</span><span class="s">"20px 0"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;mj-column&gt;</span>
        <span class="nt">&lt;mj-image</span> <span class="na">src=</span><span class="s">"https://via.placeholder.com/150"</span> <span class="na">alt=</span><span class="s">"Brand Logo"</span> <span class="na">width=</span><span class="s">"150px"</span> <span class="na">align=</span><span class="s">"center"</span><span class="nt">&gt;&lt;/mj-image&gt;</span>
      <span class="nt">&lt;/mj-column&gt;</span>
    <span class="nt">&lt;/mj-section&gt;</span>

    <span class="c">&lt;!-- Main Hero Section --&gt;</span>
    <span class="nt">&lt;mj-section</span> <span class="na">background-color=</span><span class="s">"#ff6347"</span> <span class="na">padding=</span><span class="s">"40px 0"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;mj-column&gt;</span>
        <span class="nt">&lt;mj-text</span> <span class="na">color=</span><span class="s">"#ffffff"</span> <span class="na">font-size=</span><span class="s">"30px"</span> <span class="na">font-family=</span><span class="s">"Arial, sans-serif"</span> <span class="na">align=</span><span class="s">"center"</span> <span class="na">padding=</span><span class="s">"10px 0"</span><span class="nt">&gt;</span>
          🚀 Introducing Our Latest Product!
        <span class="nt">&lt;/mj-text&gt;</span>
        <span class="nt">&lt;mj-text</span> <span class="na">color=</span><span class="s">"#ffffff"</span> <span class="na">font-size=</span><span class="s">"18px"</span> <span class="na">font-family=</span><span class="s">"Arial, sans-serif"</span> <span class="na">align=</span><span class="s">"center"</span> <span class="na">padding=</span><span class="s">"10px 0"</span><span class="nt">&gt;</span>
          Get ready to elevate your experience with the best features we’ve ever designed.
        <span class="nt">&lt;/mj-text&gt;</span>
        <span class="nt">&lt;mj-button</span> <span class="na">background-color=</span><span class="s">"#ffffff"</span> <span class="na">color=</span><span class="s">"#ff6347"</span> <span class="na">font-family=</span><span class="s">"Arial, sans-serif"</span> <span class="na">font-size=</span><span class="s">"18px"</span> <span class="na">href=</span><span class="s">"https://example.com"</span> <span class="na">padding=</span><span class="s">"15px 25px"</span> <span class="na">align=</span><span class="s">"center"</span><span class="nt">&gt;</span>
          Explore Now
        <span class="nt">&lt;/mj-button&gt;</span>
      <span class="nt">&lt;/mj-column&gt;</span>
    <span class="nt">&lt;/mj-section&gt;</span>

    <span class="c">&lt;!-- Features Section --&gt;</span>
    <span class="nt">&lt;mj-section</span> <span class="na">background-color=</span><span class="s">"#ffffff"</span> <span class="na">padding=</span><span class="s">"40px 0"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;mj-column</span> <span class="na">width=</span><span class="s">"33.33%"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;mj-text</span> <span class="na">font-size=</span><span class="s">"20px"</span> <span class="na">font-family=</span><span class="s">"Arial, sans-serif"</span> <span class="na">align=</span><span class="s">"center"</span> <span class="na">color=</span><span class="s">"#333333"</span><span class="nt">&gt;</span>
          🔥 Feature 1
        <span class="nt">&lt;/mj-text&gt;</span>
        <span class="nt">&lt;mj-text</span> <span class="na">font-size=</span><span class="s">"16px"</span> <span class="na">font-family=</span><span class="s">"Arial, sans-serif"</span> <span class="na">align=</span><span class="s">"center"</span> <span class="na">color=</span><span class="s">"#777777"</span><span class="nt">&gt;</span>
          A groundbreaking innovation that will change the way you use products forever.
        <span class="nt">&lt;/mj-text&gt;</span>
      <span class="nt">&lt;/mj-column&gt;</span>
      <span class="nt">&lt;mj-column</span> <span class="na">width=</span><span class="s">"33.33%"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;mj-text</span> <span class="na">font-size=</span><span class="s">"20px"</span> <span class="na">font-family=</span><span class="s">"Arial, sans-serif"</span> <span class="na">align=</span><span class="s">"center"</span> <span class="na">color=</span><span class="s">"#333333"</span><span class="nt">&gt;</span>
          🚀 Feature 2
        <span class="nt">&lt;/mj-text&gt;</span>
        <span class="nt">&lt;mj-text</span> <span class="na">font-size=</span><span class="s">"16px"</span> <span class="na">font-family=</span><span class="s">"Arial, sans-serif"</span> <span class="na">align=</span><span class="s">"center"</span> <span class="na">color=</span><span class="s">"#777777"</span><span class="nt">&gt;</span>
          Experience seamless integration with every device you own.
        <span class="nt">&lt;/mj-text&gt;</span>
      <span class="nt">&lt;/mj-column&gt;</span>
      <span class="nt">&lt;mj-column</span> <span class="na">width=</span><span class="s">"33.33%"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;mj-text</span> <span class="na">font-size=</span><span class="s">"20px"</span> <span class="na">font-family=</span><span class="s">"Arial, sans-serif"</span> <span class="na">align=</span><span class="s">"center"</span> <span class="na">color=</span><span class="s">"#333333"</span><span class="nt">&gt;</span>
          ✨ Feature 3
        <span class="nt">&lt;/mj-text&gt;</span>
        <span class="nt">&lt;mj-text</span> <span class="na">font-size=</span><span class="s">"16px"</span> <span class="na">font-family=</span><span class="s">"Arial, sans-serif"</span> <span class="na">align=</span><span class="s">"center"</span> <span class="na">color=</span><span class="s">"#777777"</span><span class="nt">&gt;</span>
          Enjoy the most user-friendly interface yet, designed for everyone.
        <span class="nt">&lt;/mj-text&gt;</span>
      <span class="nt">&lt;/mj-column&gt;</span>
    <span class="nt">&lt;/mj-section&gt;</span>

    <span class="c">&lt;!-- Footer Section --&gt;</span>
    <span class="nt">&lt;mj-section</span> <span class="na">background-color=</span><span class="s">"#2c3e50"</span> <span class="na">padding=</span><span class="s">"20px 0"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;mj-column&gt;</span>
        <span class="nt">&lt;mj-text</span> <span class="na">color=</span><span class="s">"#ecf0f1"</span> <span class="na">font-size=</span><span class="s">"14px"</span> <span class="na">font-family=</span><span class="s">"Arial, sans-serif"</span> <span class="na">align=</span><span class="s">"center"</span><span class="nt">&gt;</span>
          If you no longer wish to receive emails from us, you can <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"https://example.com/unsubscribe"</span> <span class="na">style=</span><span class="s">"color: #ecf0f1;"</span><span class="nt">&gt;</span>unsubscribe here<span class="nt">&lt;/a&gt;</span>.
        <span class="nt">&lt;/mj-text&gt;</span>
      <span class="nt">&lt;/mj-column&gt;</span>
    <span class="nt">&lt;/mj-section&gt;</span>
  <span class="nt">&lt;/mj-body&gt;</span>
<span class="nt">&lt;/mjml&gt;</span>
</code></pre></div></div>

<h5 id="benefits-of-using-mjml">Benefits of Using MJML</h5>

<p><code class="language-plaintext highlighter-rouge">Responsive Layout:</code> This email will adapt to different screen sizes and devices (mobile, tablet, and desktop). The content will look great no matter where it’s viewed.</p>

<p><code class="language-plaintext highlighter-rouge">Clear Call-to-Action:</code> The button in the Hero Section stands out with its contrasting color, encouraging the recipient to click and learn more about the product.</p>

<p><code class="language-plaintext highlighter-rouge">Professional Design:</code> The clean and consistent design enhances user experience while making your brand look trustworthy and modern.</p>

<p><code class="language-plaintext highlighter-rouge">Cross-Client Compatibility:</code> With MJML, you don’t need to worry about rendering issues across different email clients like Gmail, Yahoo, Outlook, and Apple Mail.</p>

<h5 id="conclusion">Conclusion</h5>
<p>MJML makes designing responsive, beautiful emails a breeze. Whether you’re a beginner or a seasoned developer, it simplifies the email creation process while ensuring high-quality results. The best part? You don’t need to worry about the headache of making your email look good on every device and email client, as MJML handles that for you.</p>

<p><code class="language-plaintext highlighter-rouge">Give it a try:</code> You can start using MJML right now by visiting their official site, where you can write, test, and compile your emails directly online!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[In today’s fast-paced digital world, email marketing has become a vital part of business communication. Whether it’s a promotional campaign, a newsletter, or a product update, having an attractive and responsive email can make a big difference. But creating fancy emails that look good across different devices and email clients can be a headache. This is where MJML comes to the rescue.]]></summary></entry><entry><title type="html">XML Import With XSLT</title><link href="https://bhupendranegi.github.io/XML-Import-With-XSLT/" rel="alternate" type="text/html" title="XML Import With XSLT" /><published>2023-01-21T00:00:00+00:00</published><updated>2023-01-21T00:00:00+00:00</updated><id>https://bhupendranegi.github.io/XML-Import-With-XSLT</id><content type="html" xml:base="https://bhupendranegi.github.io/XML-Import-With-XSLT/"><![CDATA[<hr />
<p>XSLT is a powerful tool for transforming XML data into a desired format. It can be especially useful in Rails applications when you need to:</p>

<ol>
  <li>
    <p>Transform incoming XML into a format compatible with your application’s database.</p>
  </li>
  <li>
    <p>Extract only the necessary information from complex XML documents.</p>
  </li>
  <li>
    <p>Convert XML into JSON for use in APIs or front-end applications.</p>
  </li>
  <li>
    <p>Leverage a declarative approach that separates transformation logic from application code, making it easier to maintain and modify.</p>
  </li>
</ol>

<figure class="post-figure">
  <div class="post-figure-flow">
    <div class="post-figure-step">
      <i data-lucide="file-code-2" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">XML</span>
      <span class="post-figure-step-sub">Incoming source data</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="wand-2" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">XSLT</span>
      <span class="post-figure-step-sub">Declarative stylesheet rules</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="braces" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Output</span>
      <span class="post-figure-step-sub">HTML · JSON · DB rows</span>
    </div>
  </div>
  <figcaption class="post-figure-caption">XSLT reshapes messy XML into exactly the format your app needs — without touching app code.</figcaption>
</figure>

<ol>
  <li>
    <p>Handle complex XML structures efficiently without extensive custom parsing logic.</p>
  </li>
  <li>
    <p>Reuse existing XSLT stylesheets for standardized transformations across different applications or services.</p>
  </li>
</ol>

<h5 id="sample-xml-and-xslt">Sample XML and XSLT</h5>

<p>Let’s assume you have the following XML file (data.xml):</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;products&gt;</span>
  <span class="nt">&lt;product&gt;</span>
    <span class="nt">&lt;id&gt;</span>1<span class="nt">&lt;/id&gt;</span>
    <span class="nt">&lt;name&gt;</span>Product A<span class="nt">&lt;/name&gt;</span>
    <span class="nt">&lt;price&gt;</span>100<span class="nt">&lt;/price&gt;</span>
    <span class="nt">&lt;category&gt;</span>Electronics<span class="nt">&lt;/category&gt;</span>
  <span class="nt">&lt;/product&gt;</span>
  <span class="nt">&lt;product&gt;</span>
    <span class="nt">&lt;id&gt;</span>2<span class="nt">&lt;/id&gt;</span>
    <span class="nt">&lt;name&gt;</span>Product B<span class="nt">&lt;/name&gt;</span>
    <span class="nt">&lt;price&gt;</span>200<span class="nt">&lt;/price&gt;</span>
    <span class="nt">&lt;category&gt;</span>Furniture<span class="nt">&lt;/category&gt;</span>
  <span class="nt">&lt;/product&gt;</span>
  <span class="nt">&lt;product&gt;</span>
    <span class="nt">&lt;id&gt;</span>3<span class="nt">&lt;/id&gt;</span>
    <span class="nt">&lt;name&gt;</span>Product C<span class="nt">&lt;/name&gt;</span>
    <span class="nt">&lt;price&gt;</span>150<span class="nt">&lt;/price&gt;</span>
    <span class="nt">&lt;category&gt;</span>Electronics<span class="nt">&lt;/category&gt;</span>
  <span class="nt">&lt;/product&gt;</span>
<span class="nt">&lt;/products&gt;</span>
</code></pre></div></div>

<p>And an enhanced XSLT file (transform.xslt) to transform the XML into a JSON-like format, including a condition to filter products by category:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;xsl:stylesheet</span> <span class="na">version=</span><span class="s">"1.0"</span> <span class="na">xmlns:xsl=</span><span class="s">"http://www.w3.org/1999/XSL/Transform"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;xsl:output</span> <span class="na">method=</span><span class="s">"text"</span> <span class="na">indent=</span><span class="s">"yes"</span><span class="nt">/&gt;</span>
  <span class="nt">&lt;xsl:template</span> <span class="na">match=</span><span class="s">"/"</span><span class="nt">&gt;</span>
    [
    <span class="nt">&lt;xsl:for-each</span> <span class="na">select=</span><span class="s">"products/product[category='Electronics']"</span><span class="nt">&gt;</span>
      {
        "id": "<span class="nt">&lt;xsl:value-of</span> <span class="na">select=</span><span class="s">"id"</span><span class="nt">/&gt;</span>",
        "name": "<span class="nt">&lt;xsl:value-of</span> <span class="na">select=</span><span class="s">"name"</span><span class="nt">/&gt;</span>",
        "price": "<span class="nt">&lt;xsl:value-of</span> <span class="na">select=</span><span class="s">"price"</span><span class="nt">/&gt;</span>",
        "category": "<span class="nt">&lt;xsl:value-of</span> <span class="na">select=</span><span class="s">"category"</span><span class="nt">/&gt;</span>"
      }<span class="nt">&lt;xsl:if</span> <span class="na">test=</span><span class="s">"position() != last()"</span><span class="nt">&gt;</span>,<span class="nt">&lt;/xsl:if&gt;</span>
    <span class="nt">&lt;/xsl:for-each&gt;</span>
    ]
  <span class="nt">&lt;/xsl:template&gt;</span>
<span class="nt">&lt;/xsl:stylesheet&gt;</span>
</code></pre></div></div>

<p>This XSLT transformation filters products to include only those in the “Electronics” category. You can further customize the conditions, such as filtering by price range or combining multiple criteria.</p>

<h5 id="implementation">Implementation</h5>
<p><code class="language-plaintext highlighter-rouge">Create a Service Object</code></p>

<p>To keep your code clean, encapsulate the logic in a service object. Create a file app/services/xml_importer.rb:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s1">'nokogiri'</span>

<span class="k">class</span> <span class="nc">XmlImporter</span>
  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">import</span><span class="p">(</span><span class="n">xml_path</span><span class="p">,</span> <span class="n">xslt_path</span><span class="p">)</span>
    <span class="n">xml</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="n">xml_path</span><span class="p">)</span>
    <span class="n">xslt</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="n">xslt_path</span><span class="p">)</span>

    <span class="n">doc</span> <span class="o">=</span> <span class="no">Nokogiri</span><span class="o">::</span><span class="no">XML</span><span class="p">(</span><span class="n">xml</span><span class="p">)</span>
    <span class="n">stylesheet</span> <span class="o">=</span> <span class="no">Nokogiri</span><span class="o">::</span><span class="no">XSLT</span><span class="p">(</span><span class="n">xslt</span><span class="p">)</span>

    <span class="n">transformed_data</span> <span class="o">=</span> <span class="n">stylesheet</span><span class="p">.</span><span class="nf">transform</span><span class="p">(</span><span class="n">doc</span><span class="p">)</span>
    <span class="no">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">transformed_data</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Generate a controller to handle XML imports:</code></p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">rails</span> <span class="n">generate</span> <span class="n">controller</span> <span class="no">Imports</span>
</code></pre></div></div>

<p>In app/controllers/imports_controller.rb, add the following code:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">class</span> <span class="nc">ImportsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
  <span class="k">def</span> <span class="nf">create</span>
    <span class="n">xml_path</span> <span class="o">=</span> <span class="no">Rails</span><span class="p">.</span><span class="nf">root</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s1">'data.xml'</span><span class="p">)</span>
    <span class="n">xslt_path</span> <span class="o">=</span> <span class="no">Rails</span><span class="p">.</span><span class="nf">root</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s1">'transform.xslt'</span><span class="p">)</span>

    <span class="vi">@data</span> <span class="o">=</span> <span class="no">XmlImporter</span><span class="p">.</span><span class="nf">import</span><span class="p">(</span><span class="n">xml_path</span><span class="p">,</span> <span class="n">xslt_path</span><span class="p">)</span>

    <span class="n">render</span> <span class="ss">json: </span><span class="vi">@data</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">Set Up Routes</code></p>

<p>Add a route for the import action in config/routes.rb:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span>
  <span class="n">post</span> <span class="s1">'imports'</span><span class="p">,</span> <span class="ss">to: </span><span class="s1">'imports#create'</span>
<span class="k">end</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Testing the Implementation</code></p>

<p>Place the data.xml and transform.xslt files in the Rails root directory. Start the Rails server:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">rails</span> <span class="n">server</span>
</code></pre></div></div>

<p>Use a tool like curl or Postman to test the endpoint:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-X</span> POST http://localhost:3000/imports
</code></pre></div></div>

<p>You should receive a JSON response with the transformed data:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>
  <span class="o">{</span><span class="s2">"id"</span>: <span class="s2">"1"</span>, <span class="s2">"name"</span>: <span class="s2">"Product A"</span>, <span class="s2">"price"</span>: <span class="s2">"100"</span>, <span class="s2">"category"</span>: <span class="s2">"Electronics"</span><span class="o">}</span>,
  <span class="o">{</span><span class="s2">"id"</span>: <span class="s2">"3"</span>, <span class="s2">"name"</span>: <span class="s2">"Product C"</span>, <span class="s2">"price"</span>: <span class="s2">"150"</span>, <span class="s2">"category"</span>: <span class="s2">"Electronics"</span><span class="o">}</span>
<span class="o">]</span>
</code></pre></div></div>
<h5 id="conclusion">Conclusion</h5>

<p>By leveraging XSLT and Nokogiri, you can easily transform and import XML data into your Rails application. This approach is powerful, flexible, and allows for clean separation of transformation logic from application code. Experiment with different XML and XSLT structures to unlock the full potential of this technique!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[XSLT is a powerful tool for transforming XML data into a desired format. It can be especially useful in Rails applications when you need to:]]></summary></entry><entry><title type="html">Efficient Data Fetching with React Query</title><link href="https://bhupendranegi.github.io/Efficient-Data-Fetching-With-React-Query/" rel="alternate" type="text/html" title="Efficient Data Fetching with React Query" /><published>2022-03-07T00:00:00+00:00</published><updated>2022-03-07T00:00:00+00:00</updated><id>https://bhupendranegi.github.io/Efficient-Data-Fetching-With-React-Query</id><content type="html" xml:base="https://bhupendranegi.github.io/Efficient-Data-Fetching-With-React-Query/"><![CDATA[<hr />
<p>React Query is a powerful library for managing server-state in React applications. It simplifies data fetching, caching, and synchronization, making your app faster and easier to maintain. In this blog, we’ll explore how to use React Query to fetch and cache data with a practical example.</p>

<figure class="post-figure">
  <div class="post-figure-flow">
    <div class="post-figure-step">
      <i data-lucide="app-window" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Component</span>
      <span class="post-figure-step-sub">Calls useQuery()</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="database" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">Query cache</span>
      <span class="post-figure-step-sub">Instant data, then revalidates</span>
    </div>
    <div class="post-figure-arrow"><i data-lucide="arrow-right" class="site-icon" aria-hidden="true"></i></div>
    <div class="post-figure-step">
      <i data-lucide="server" class="site-icon" aria-hidden="true"></i>
      <span class="post-figure-step-label">API / Server</span>
      <span class="post-figure-step-sub">Fetched on miss or stale</span>
    </div>
  </div>
  <figcaption class="post-figure-caption">React Query serves cached data instantly, then refetches in the background to keep it fresh.</figcaption>
</figure>

<h5 id="why-use-react-query">Why Use React Query?</h5>

<p><code class="language-plaintext highlighter-rouge">Automatic Caching:</code> React Query caches data automatically and updates it when necessary.</p>

<p><code class="language-plaintext highlighter-rouge">Out-of-the-Box Features:</code> It provides features like retries, background refetching, and stale data handling.</p>

<p><code class="language-plaintext highlighter-rouge">Simplifies State Management:</code> Avoids the need for global state management for server data.</p>

<p><code class="language-plaintext highlighter-rouge">Customizable and Flexible:</code> You can fine-tune how data is fetched, cached, and synchronized.</p>

<h5 id="setting-up-react-query">Setting Up React Query</h5>

<p>First, install the necessary package:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm <span class="nb">install</span> @tanstack/react-query
</code></pre></div></div>

<p>You’ll also need React Query Devtools for debugging:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm <span class="nb">install</span> @tanstack/react-query-devtools
</code></pre></div></div>

<h5 id="configure-queryclient">Configure QueryClient</h5>

<p>Set up a QueryClient to manage your queries. Wrap your app with the QueryClientProvider:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">react</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">QueryClient</span><span class="p">,</span> <span class="nx">QueryClientProvider</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@tanstack/react-query</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">ReactQueryDevtools</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@tanstack/react-query-devtools</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">queryClient</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">QueryClient</span><span class="p">();</span>

<span class="kd">function</span> <span class="nf">App</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">return </span><span class="p">(</span>
    <span class="o">&lt;</span><span class="nx">QueryClientProvider</span> <span class="nx">client</span><span class="o">=</span><span class="p">{</span><span class="nx">queryClient</span><span class="p">}</span><span class="o">&gt;</span>
      <span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span>
        <span class="o">&lt;</span><span class="nx">h1</span><span class="o">&gt;</span><span class="nx">React</span> <span class="nx">Query</span> <span class="nx">Example</span><span class="o">&lt;</span><span class="sr">/h1</span><span class="err">&gt;
</span>        <span class="o">&lt;</span><span class="nx">MyComponent</span> <span class="o">/&gt;</span>
      <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span>      <span class="o">&lt;</span><span class="nx">ReactQueryDevtools</span> <span class="nx">initialIsOpen</span><span class="o">=</span><span class="p">{</span><span class="kc">false</span><span class="p">}</span> <span class="sr">/</span><span class="err">&gt;
</span>    <span class="o">&lt;</span><span class="sr">/QueryClientProvider</span><span class="err">&gt;
</span>  <span class="p">);</span>
<span class="p">}</span>

<span class="k">export</span> <span class="k">default</span> <span class="nx">App</span><span class="p">;</span>
</code></pre></div></div>

<h5 id="fetching-and-caching-data">Fetching and Caching Data</h5>

<p>Let’s fetch and display a list of users from a public API.</p>

<p>Create a function to fetch data from the API:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">fetchUsers</span> <span class="o">=</span> <span class="k">async </span><span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">fetch</span><span class="p">(</span><span class="dl">'</span><span class="s1">https://jsonplaceholder.typicode.com/users</span><span class="dl">'</span><span class="p">);</span>
  <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="nx">response</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nc">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Network response was not ok</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nx">response</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span>
<span class="p">};</span>
</code></pre></div></div>

<h5 id="use-usequery-to-fetch-data">Use useQuery to Fetch Data</h5>

<p>In your component, use the useQuery hook to fetch and cache the data:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">react</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">useQuery</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@tanstack/react-query</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">function</span> <span class="nf">MyComponent</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="p">{</span> <span class="nx">data</span><span class="p">,</span> <span class="nx">error</span><span class="p">,</span> <span class="nx">isLoading</span> <span class="p">}</span> <span class="o">=</span> <span class="nf">useQuery</span><span class="p">([</span><span class="dl">'</span><span class="s1">users</span><span class="dl">'</span><span class="p">],</span> <span class="nx">fetchUsers</span><span class="p">);</span>

  <span class="k">if </span><span class="p">(</span><span class="nx">isLoading</span><span class="p">)</span> <span class="k">return</span> <span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span><span class="nx">Loading</span><span class="p">...</span><span class="o">&lt;</span><span class="sr">/div&gt;</span><span class="err">;
</span>  <span class="k">if </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="k">return</span> <span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span><span class="nb">Error</span><span class="p">:</span> <span class="p">{</span><span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">}</span><span class="o">&lt;</span><span class="sr">/div&gt;</span><span class="err">;
</span>
  <span class="k">return </span><span class="p">(</span>
    <span class="o">&lt;</span><span class="nx">ul</span><span class="o">&gt;</span>
      <span class="p">{</span><span class="nx">data</span><span class="p">.</span><span class="nf">map</span><span class="p">((</span><span class="nx">user</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">(</span>
        <span class="o">&lt;</span><span class="nx">li</span> <span class="nx">key</span><span class="o">=</span><span class="p">{</span><span class="nx">user</span><span class="p">.</span><span class="nx">id</span><span class="p">}</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">user</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span><span class="o">&lt;</span><span class="sr">/li</span><span class="err">&gt;
</span>      <span class="p">))}</span>
    <span class="o">&lt;</span><span class="sr">/ul</span><span class="err">&gt;
</span>  <span class="p">);</span>
<span class="p">}</span>

<span class="k">export</span> <span class="k">default</span> <span class="nx">MyComponent</span><span class="p">;</span>
</code></pre></div></div>

<h5 id="configure-query-options">Configure Query Options</h5>

<p>React Query offers several options to fine-tune behavior:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="p">{</span> <span class="nx">data</span><span class="p">,</span> <span class="nx">error</span><span class="p">,</span> <span class="nx">isLoading</span> <span class="p">}</span> <span class="o">=</span> <span class="nf">useQuery</span><span class="p">([</span><span class="dl">'</span><span class="s1">users</span><span class="dl">'</span><span class="p">],</span> <span class="nx">fetchUsers</span><span class="p">,</span> <span class="p">{</span>
  <span class="na">staleTime</span><span class="p">:</span> <span class="mi">60000</span><span class="p">,</span> <span class="c1">// Data is considered fresh for 60 seconds</span>
  <span class="na">cacheTime</span><span class="p">:</span> <span class="mi">300000</span><span class="p">,</span> <span class="c1">// Cache data for 5 minutes</span>
  <span class="na">retry</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="c1">// Retry failed requests up to 2 times</span>
  <span class="na">refetchOnWindowFocus</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="c1">// Disable refetching on window focus</span>
<span class="p">});</span>
</code></pre></div></div>

<h5 id="pagination-and-infinite-queries">Pagination and Infinite Queries</h5>

<p>React Query supports pagination and infinite scrolling with the useInfiniteQuery hook. For example:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">useInfiniteQuery</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@tanstack/react-query</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">fetchUsersPage</span> <span class="o">=</span> <span class="k">async </span><span class="p">({</span> <span class="nx">pageParam</span> <span class="o">=</span> <span class="mi">1</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">fetch</span><span class="p">(</span><span class="s2">`https://jsonplaceholder.typicode.com/users?_page=</span><span class="p">${</span><span class="nx">pageParam</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
  <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="nx">response</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nc">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Network response was not ok</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nx">response</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span>
<span class="p">};</span>

<span class="kd">function</span> <span class="nf">PaginatedComponent</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="p">{</span>
    <span class="nx">data</span><span class="p">,</span>
    <span class="nx">fetchNextPage</span><span class="p">,</span>
    <span class="nx">hasNextPage</span><span class="p">,</span>
    <span class="nx">isFetchingNextPage</span><span class="p">,</span>
  <span class="p">}</span> <span class="o">=</span> <span class="nf">useInfiniteQuery</span><span class="p">([</span><span class="dl">'</span><span class="s1">users</span><span class="dl">'</span><span class="p">],</span> <span class="nx">fetchUsersPage</span><span class="p">,</span> <span class="p">{</span>
    <span class="na">getNextPageParam</span><span class="p">:</span> <span class="p">(</span><span class="nx">lastPage</span><span class="p">,</span> <span class="nx">allPages</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">(</span><span class="nx">lastPage</span><span class="p">.</span><span class="nx">length</span> <span class="p">?</span> <span class="nx">allPages</span><span class="p">.</span><span class="nx">length</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">:</span> <span class="kc">undefined</span><span class="p">),</span>
  <span class="p">});</span>

  <span class="k">return </span><span class="p">(</span>
    <span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span>
      <span class="p">{</span><span class="nx">data</span><span class="p">?.</span><span class="nx">pages</span><span class="p">.</span><span class="nf">map</span><span class="p">((</span><span class="nx">page</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">(</span>
        <span class="o">&lt;</span><span class="nx">ul</span> <span class="nx">key</span><span class="o">=</span><span class="p">{</span><span class="nx">i</span><span class="p">}</span><span class="o">&gt;</span>
          <span class="p">{</span><span class="nx">page</span><span class="p">.</span><span class="nf">map</span><span class="p">((</span><span class="nx">user</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">(</span>
            <span class="o">&lt;</span><span class="nx">li</span> <span class="nx">key</span><span class="o">=</span><span class="p">{</span><span class="nx">user</span><span class="p">.</span><span class="nx">id</span><span class="p">}</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">user</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span><span class="o">&lt;</span><span class="sr">/li</span><span class="err">&gt;
</span>          <span class="p">))}</span>
        <span class="o">&lt;</span><span class="sr">/ul</span><span class="err">&gt;
</span>      <span class="p">))}</span>
      <span class="o">&lt;</span><span class="nx">button</span>
        <span class="nx">onClick</span><span class="o">=</span><span class="p">{()</span> <span class="o">=&gt;</span> <span class="nf">fetchNextPage</span><span class="p">()}</span>
        <span class="nx">disabled</span><span class="o">=</span><span class="p">{</span><span class="o">!</span><span class="nx">hasNextPage</span> <span class="o">||</span> <span class="nx">isFetchingNextPage</span><span class="p">}</span>
      <span class="o">&gt;</span>
        <span class="p">{</span><span class="nx">isFetchingNextPage</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">Loading...</span><span class="dl">'</span> <span class="p">:</span> <span class="nx">hasNextPage</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">Load More</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">No More Users</span><span class="dl">'</span><span class="p">}</span>
      <span class="o">&lt;</span><span class="sr">/button</span><span class="err">&gt;
</span>    <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span>  <span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h5 id="mutations">Mutations</h5>

<p>Mutations are used to create, update, or delete data. For example:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">useMutation</span><span class="p">,</span> <span class="nx">useQueryClient</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@tanstack/react-query</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">addUser</span> <span class="o">=</span> <span class="k">async </span><span class="p">(</span><span class="nx">newUser</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">fetch</span><span class="p">(</span><span class="dl">'</span><span class="s1">https://jsonplaceholder.typicode.com/users</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>
    <span class="na">method</span><span class="p">:</span> <span class="dl">'</span><span class="s1">POST</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">headers</span><span class="p">:</span> <span class="p">{</span> <span class="dl">'</span><span class="s1">Content-Type</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">application/json</span><span class="dl">'</span> <span class="p">},</span>
    <span class="na">body</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">newUser</span><span class="p">),</span>
  <span class="p">});</span>
  <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="nx">response</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nc">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Network response was not ok</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nx">response</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span>
<span class="p">};</span>

<span class="kd">function</span> <span class="nf">AddUserComponent</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">queryClient</span> <span class="o">=</span> <span class="nf">useQueryClient</span><span class="p">();</span>
  <span class="kd">const</span> <span class="nx">mutation</span> <span class="o">=</span> <span class="nf">useMutation</span><span class="p">(</span><span class="nx">addUser</span><span class="p">,</span> <span class="p">{</span>
    <span class="na">onSuccess</span><span class="p">:</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
      <span class="nx">queryClient</span><span class="p">.</span><span class="nf">invalidateQueries</span><span class="p">([</span><span class="dl">'</span><span class="s1">users</span><span class="dl">'</span><span class="p">]);</span> <span class="c1">// Refetch users after adding</span>
    <span class="p">},</span>
  <span class="p">});</span>

  <span class="k">return </span><span class="p">(</span>
    <span class="o">&lt;</span><span class="nx">button</span>
      <span class="nx">onClick</span><span class="o">=</span><span class="p">{()</span> <span class="o">=&gt;</span> <span class="nx">mutation</span><span class="p">.</span><span class="nf">mutate</span><span class="p">({</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">New User</span><span class="dl">'</span><span class="p">,</span> <span class="na">email</span><span class="p">:</span> <span class="dl">'</span><span class="s1">newuser@example.com</span><span class="dl">'</span> <span class="p">})}</span>
    <span class="o">&gt;</span>
      <span class="nx">Add</span> <span class="nx">User</span>
    <span class="o">&lt;</span><span class="sr">/button</span><span class="err">&gt;
</span>  <span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h5 id="conclusion">Conclusion</h5>

<p>React Query is a game-changer for managing server-state in React applications. Its features like automatic caching, background refetching, and retry logic reduce boilerplate and improve performance. Start using React Query today to streamline your data-fetching logic and enhance your app’s user experience!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[React Query is a powerful library for managing server-state in React applications. It simplifies data fetching, caching, and synchronization, making your app faster and easier to maintain. In this blog, we’ll explore how to use React Query to fetch and cache data with a practical example.]]></summary></entry></feed>