TL;DR: Üretim aşamasında bir Laravel 12 + React 19 + Inertia v2 uygulamasında geçirdiğim haftalar boyunca, teşhisi zor pek çok hatayla karşılaştım: üst üste gelen ziyaret iptalleri, dağıtım zamanındaki geçici parçacık bozulmaları, zayıf varsayılan hata kullanıcı deneyimi ve çerçeveye özel geçici çözüm kodları. Bu makale açık sözlü, ama kapsamlı: gerçek bir yığın içinde gözlemlenen davranışlar, belgeler/sorunlarla desteklenmiştir.
<hr/>
<h2>
<a name="scope-what-this-is-what-this-isnt" href="#scope-what-this-is-what-this-isnt">
</a>
Kapsam (Bu Nedir, Bu Değil)
</h2>
<p>Bu <strong>her</strong> projede Inertia'nın başarısız olduğu iddiası değil. Birçok ekip, CRUD-ağır yönetici uygulamalarında Inertia'yı başarıyla kullanıyor. </p>
<p>Bu <strong>bir</strong> gerçek üretim kurulumu ile aktif kullanıcılar ve sık dağıtımlarla, Inertia'nın yönlendirici soyutlamasının sürekli operasyonel acı ve belli belirsiz hata modelleri yarattığı iddiasıdır.</p>
<p>Devam eden ortam:</p>
<ul>
<li>Laravel 12</li>
<li>React 19</li>
<li>Inertia.js v2</li>
<li>Vite kod bölünmesi</li>
<li>Bazı ortamlardaki yerinde değiştirme tarzı dağıtımlar</li>
</ul>
<h2>
<a name="the-pitch-vs-the-reality" href="#the-pitch-vs-the-reality">
</a>
İddia ve Gerçeklik
</h2>
<p>Inertia'nın temel iddiası güçlüdür: Routine web gezintisi için ayrı bir genel API yüzeyi yönetmeden, SPA benzeri UX oluşturun.</p>
<p>Problem, iş akışları karmaşık hale geldiğinde başladı: çok adımlı eylemler, dağıtım karmaşası ve kenar vakası hata yönetimi.</p>
<h2>
<a name="1-sequential-request-pitfall-raw-await-endraw-does-not-serialize-inertia-router-visits" href="#1-sequential-request-pitfall-raw-await-endraw-does-not-serialize-inertia-router-visits">
</a>
1. Sıralı İstek Tuzakları: <code>await</code> Inertia Yönlendirme Ziyaretlerini Serialize Etmez
</h2>
<p>Uygulamamızda, bir işçi atamak için iki sıralı işlem gereklik gösterdi:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight typescript"><code><span class="kd">const</span> <span class="nx">handleAssign</span> <span class="o">=</span> <span class="k">async </span><span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
// Adım 1: İşçiyi ata
await router.put(/admin/tasks/</span><span class="p">${</span><span class="nx">task</span><span class="p">.</span><span class="nx">id</span><span class="p">}</span><span class="s2">/assign, {
assignee_id: Number(selectedUserId)
})
// Adım 2: Durumu güncelle
await router.put(/admin/tasks/</span><span class="p">${</span><span class="nx">task</span><span class="p">.</span><span class="nx">id</span><span class="p">}</span><span class="s2">/status, {
status: ‘In Progress‘
})
setModalOpen(false)
}
<p>Promise tabanlı istemciler (<code>fetch</code>, <code>axios</code>) ile bu şekil, katı sıralama gerektirir.</p>
<p>Durumumuzda gözlemlenen sonuç ise:</p>
<ul>
<li>durum güncellendi</li>
<li>atama gerçekleşmedi</li>
<li>ilk istek ağda iptal edildi olarak gösterildi</li>
<li>varsayılan olarak, belirgin bir uygulama düzeyinde hata ortaya çıkmadı</li>
</ul>
<p>Bunun neden olabileceği:</p>
<ul>
<li>Inertia yönlendirme yöntemleri, bu kodun varsaydığı şekilde Promise döndürmüyor</li>
<li><code>await</code> bu nedenle istek tamamlama sırasını garanti etmez</li>
<li>üst üste gelen ziyaretler önceki ziyaretleri iptal edebilir (<a href="https://github.com/inertiajs/inertia/discussions/1471" target="_blank" rel="noopener noreferrer">tasarım gereği</a>)</li>
</ul>
<p>Topluluk tartışmaları: <a href="https://github.com/inertiajs/inertia/discussions/1918" target="_blank" rel="noopener noreferrer">Promise desteği kasıtlı olarak kaldırılmıştır</a>, <a href="https://github.com/inertiajs/inertia/discussions/1765" target="_blank" rel="noopener noreferrer">bunun için yıllardır talepler var</a>.</p>
<p>Çalışan bir kalıp, geri çağırma zincirleme olmaktı:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight typescript"><code><span class="kd">const</span> <span class="nx">handleAssign</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
router.put(/admin/tasks/</span><span class="p">${</span><span class="nx">task</span><span class="p">.</span><span class="nx">id</span><span class="p">}</span><span class="s2">/assign, {
assignee_id: Number(selectedUserId)
}, {
onSuccess: () => {
router.put(/admin/tasks/</span><span class="p>${</span><span class="nx">task</span><span class="p">.</span><span class="nx">id</span><span class="p">}</span><span class="s2">/status, {
status: ‘In Progress‘
}, {
onSuccess: () => setModalOpen(false)
})
}
})
}
<p>Ya da ziyaretleri manuel olarak bir Promise içinde sarmalamak:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight typescript"><code><span class="k">await</span> <span class="k">new</span> <span class="nc">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
router.patch(route(‘profile.update‘), data, {
onSuccess: resolve,
onError: reject,
})
})
<p>Bu tam olarak hayal kırıklığının zirveye çıktığı yerdir: normal asenkron/await HTTP gibi görünen kod, normal asenkron/await HTTP değildir.</p>
<h2>
<a name="2-deploytime-stale-chunks-not-unique-to-inertia-but-operationally-sharper-with-serverclient-coupling" href="#2-deploytime-stale-chunks-not-unique-to-inertia-but-operationally-sharper-with-serverclient-coupling">
</a>
2. Dağıtım Zamanındaki Geçici Parçacıklar: Inertia'ya Özgü Değil, Ama Sunucu-Müşteri Bağlantısıyla İşlevselliği Daha Keskin
</h2>
<p>Her kod bölmeli SPA, dağıtım sonrasında geçici parçacık sorunları yaşayabilir. Bu Inertia'ya özel değildir.</p>
<p>Inertia, kurulumumuzda etkisini artırdı çünkü navigasyon, sunucu tarafı bileşen çözümlemesi ile istemci tarafı parçacık ithalatına bağımlıdır.</p>
<p>Temsili parçacık isimleri:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight plaintext"><code>assets/bookings-show-A3f8kQ2.js
assets/profile-Bp7mXn1.js
assets/schedule-Ck9pLw4.js
<p>Dağıtım sonrası:</p>
<ul>
<li>sunucu en son bileşen manifestosunu referans alır</li>
<li>istemci sekmesi hâlâ eski çalışma varsayımlarıyla kalabilir</li>
<li>gerekli parçacık ithalatı bozulursa, kullanıcının "ölü" navigasyon algılaması olur</li>
<li>sert yükleme gerektirir</li>
</ul>
<p>Önemli nüans:</p>
<ul>
<li>Immutable artifact / <a href="https://vercel.com/blog/version-skew-protection" target="_blank" rel="noopener noreferrer">skew-protected platforms</a> etkisini azaltır.</li>
<li>Yerinde değiştirme dağıtımları risk pencere süresini artırır.</li>
<li>Önbellek ve dağıtım stratejisi, çerçeve seçimi kadar önemlidir.</li>
</ul>
<p>Referanslar: <a href="https://inertiajs.com/asset-versioning" target="_blank" rel="noopener noreferrer">Inertia varlık sürümleme / 409</a>, <a href="https://github.com/inertiajs/inertia-laravel/issues/525" target="_blank" rel="noopener noreferrer">409 döngü raporu</a>.</p>
<p>Önemli hassasiyet:</p>
<ul>
<li>Her dağıtımda, her sekmeyi öldüren bir durum olmadığını iddia etmiyorum.</li>
<li>Çevremizde tekrar eden bir üretim olayı örüntüsü olarak iddia ediyorum.</li>
</ul>
<h2>
<a name="3-failure-ux-defaults-to-silence" href="#3-failure-ux-defaults-to-silence">
</a>
3. Hata UX Varsayılan Olarak Sessizdir
</h2>
<p>Uygulamamızda, hataları görünür/kurtarılabilir hale getirmek için açık koruyucu kurallar ekledik.<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight typescript"><code><span class="c1">// Navigasyon istisnalarını yakala ve zorla yenile</span>
router.on(‘exception‘, (event) => {
event.preventDefault();
window.location.href = event.detail.url || window.location.href;
});
// Proaktif manifest kayması kontrolü
let manifest: string | null = null;
fetch(‘/build/manifest.json‘)
.then(res => res.text())
.then(text => { manifest = text; })
.catch(() => {});
document.addEventListener(‘visibilitychange‘, async () => {
if (document.visibilityState !== ‘visible‘ || !manifest) return;
try {
const res = await fetch(‘/build/manifest.json‘, { cache: ‘no-store‘ });
if (await res.text() !== manifest) window.location.reload();
} catch {}
});
<p>Bu önlemler işe yaradı. Aynı zamanda yazmanız gereken çerçeveye özgü operasyonel borçlardır.</p>
<h2>
<a name="4-navigation-errors-vanish-without-a-trace" href="#4-navigation-errors-vanish-without-a-trace">
</a>
4. Navigasyon Hataları İzi Olmadan Yok Olur
</h2>
<p>Bir JavaScript hatası, hedef sayfa bileşeninde meydana geldiğinde, <a href="https://github.com/inertiajs/inertia/issues/710" target="_blank" rel="noopener noreferrer">navigasyon sessizce başarısız olur</a>. Önceki sayfa görünmeye devam eder. Hiçbiri hata mesajı, konsol uyarısı, duraksayan bir yükleme göstergesi yok. Kullanıcı bir bağlantıya tıklar, bekler ve hiçbir şey olmaz.</p>
<p>Sunucu hataları meydana geldiğinde, Inertia'nın varsayılan davranışı, <a href="https://mnapoli.fr/fixing-inertia-error-handling" target="_blank" rel="noopener noreferrer">tüm hata yanıtını bir modal örtüsü içinde render etmektir</a>. Geliştirme aşamasında, bu, uygulamanızın üzerinde bir modal olarak tam Laravel hata sayfasıdır. Üretimde, genel bir HTML hata sayfası — hâlâ bir modalda, hâlâ tuhaf bir kullanıcı deneyimi. Bunu düzeltmek için istisna işlemcisini JSON döndürecek şekilde geçersiz kılmalısınız, ardından bunu istemci tarafında toast bildirimleri ile yakalaması gerekir. Daha fazla geçici çözüm kodu.</p>
<p>Çalıştığım kod tabanında hem <code>router.reload()</code> hem de <code>window.location.href</code> kullanıldığı görüldü; ikincisinin kullanılması, geliştiricilerin belirli akışlar için Inertia yönlendiricisinden vazgeçtiği anlamına gelir. Bu ayrım mantıklı olabilir, ancak aynı zamanda mühendislerin iki etkileşim kalıbını öğrenmeleri gerektiği anlamına gelir.</p>
<h2>
<a name="5-props-in-html-not-unique-still-a-real-discipline-requirement" href="#5-props-in-html-not-unique-still-a-real-discipline-requirement">
</a>
5. HTML'de Props: Özgü Değil, Hâlâ Gerçek Bir Disiplin Gereksinimi
</h2>
<p>Bu, yalnızca Inertia'nın güvenlik hikayesi değildir. Herhangi bir istemciyle teslim edilen veri istemci tarafında görünür.</p>
<p>Yine de, Inertia ile <code>data-page</code> içine serileştirilen props'lar, ekiplerin dikkatsiz olması durumunda aşırı paylaşımı kolaylaştırır.</p>
<p>Referanslar: <a href="https://github.com/inertiajs/inertia/discussions/1430" target="_blank" rel="noopener noreferrer">props sayfa kaynağında görünür</a>, <a href="https://github.com/inertiajs/inertia/issues/247" target="_blank" rel="noopener noreferrer">çıkıştan sonra önbellekli hassas veriler</a>.</p>
<p>Savunulabilir bir ifade: her prop'u halka açık çıktı olarak kabul edin; istemci yüklerinde ifşa etmeyeceğiniz verileri asla eklemeyin.</p>
<h2>
<a name="6-no-api-is-better-framed-as-a-starting-optimization-not-a-permanent-architecture" href="#6-no-api-is-better-framed-as-a-starting-optimization-not-a-permanent-architecture">
</a>
6. "No API" Daha Fazla Bir Başlangıç Optimizasyonu Olarak Çerçevelenmelidir, Kalıcı Bir Mimar olarak Değil
</h2>
<p>Pazarlama söylemi başlangıçta faydalı olabilir: web gezintisi için daha az hareketli parça.</p>
<p>Pek çok gerçek sistemde, ekipler yine de açık API uç noktaları ekliyorlar:</p>
<ul>
<li>üçüncü taraf entegrasyonları ve webhook'lar</li>
<li>mobil istemciler</li>
<li>arka plan iş akışları</li>
<li>özel, güçlü sıralı etkileşimler</li>
</ul>
<p>Doğruluk için önemli düzeltmeler:</p>
<ul>
<li>Inertia, dosya yüklemelerini ve <code>FormData</code> kalıplarını destekler.</li>
<li>Ekibimiz, yerel güvenilirlik/kontrol nedenleri için bazı yükleme yollarında doğrudan <code>fetch()</code> kullandı.</li>
<li>Bu, proje düzeyinde bir takas, Inertia'nın dosya yükleyemeyeceği anlamına gelmez.</li>
</ul>
<h2>
<a name="the-root-problem-in-this-stack" href="#the-root-problem-in-this-stack">
</a>
Bu Yığındaki Temel Problem
</h2>
<p>Tekrar eden maliyet, anlam uyumsuzluğuydu:</p>
<ul>
<li>kod normal Promise tabanlı HTTP akışı gibi görünüyordu</li>
<li>çalışma zamanı davranışı yönlendirme ziyareti anlamlarına uyuyordu</li>
<li>hata, üretim koşullarında yüzeye çıkıyor, mutlu yol demolarında değil</li>
</ul>
<p>Bu uyumsuzluk, hata ayıklama süresini tüketti ve çoğu geliştiricinin "basit SPA yönlendirmesinden" beklediğinden daha fazla savunucu kalıp gerektirdi.</p>
<h2>
<a name="the-alternative-we-prefer-in-highcomplexity-flows" href="#the-alternative-we-prefer-in-highcomplexity-flows">
</a>
Yüksek Karmaşıklık Akışlarında Tercih Ettiğimiz Alternatif
</h2>
<p>Kritik sıralı işlemler için, açık HTTP daha kolay düşünülüyordu:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight typescript"><code><span class="kd">const</span> <span class="nx">handleAssign</span> <span class="o">=</span> <span class="k">async </span><span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
await fetch(/api/tasks/</span><span class="p">${</span><span class="nx">task</span><span class="p">.</span><span class="nx">id</span><span class="p">}</span><span class="s2">/assign, {
method: ‘PUT‘,
headers: { ‘Content-Type‘: ‘application/json‘ },
body: JSON.stringify({ assignee_id: Number(selectedUserId) })
})
await fetch(/api/tasks/</span><span class="p>${</span><span class="nx">task</span><span class="p">.</span><span class="nx">id</span><span class="p">}</span><span class="s2">/status, {
method: ‘PUT‘,
headers: { ‘Content-Type‘: ‘application/json‘ },
body: JSON.stringify({ status: <span class=”dl>’In Progress<span class=”dl>’ })
})
setModalOpen(false)
}