jobs.at Recruiting GmbH
SPA without API
Description
Jürgen Ratzenböck von jobs.at spricht in seinem devjobs.at TechTalk "SPA without API" darüber, welche Vorteile und Challenges dieser Ansatz mit sich bringt und gibt technische Einblicke in das Thema.
Beim Videoaufruf stimmst Du der Datenübermittlung an YouTube und der Datenschutzerklärung zu.
Video Zusammenfassung
In „SPA without API“ erklärt Jürgen Ratzenböck, wie man auf einer bestehenden serverseitig gerenderten Laravel-Anwendung mit Vue.js eine SPA-ähnliche UX realisiert, ohne eine separate REST-/GraphQL-API aufzubauen. Er zeigt Inertia.js als Adapter, der serverseitiges Routing, Controller, Validation und Session-Auth beibehält, JSON mit Component und Props liefert und auf dem Client Vue-Komponenten inkl. Navigation, Formular-Posts ($inertia.post), Redirects, Flash-/Validierungsfehlern und Asset-Versionierung rendert. Die Quintessenz: APIs bringen spürbaren Overhead (zwei Repositories, langsamer Initial-Load, HTTP-Overhead, Auth/CORS/CSP, Build- und Deployment-Aufwand, SEO), während Inertia diese Komplexität für eine einzelne Web‑App im Monorepo senkt; bei mehreren Clients bleibt eine echte API sinnvoll.
SPA ohne API: Wie „SPA without API“ von Jürgen Ratzenböck (jobs.at Recruiting GmbH) Laravel und Vue mit Inertia.js zusammenbringt
Kontext: Ein wachsendes Jobportal und ein technischer Entscheidungsweg
In „SPA without API“ zeigte Jürgen Ratzenböck von der jobs.at Recruiting GmbH, wie sein Team eine Single-Page-Application-Erfahrung in eine bestehende, serverseitig gerenderte Laravel-Anwendung integrierte – ohne eine separate REST- oder GraphQL-API aufzubauen. Aus Sicht der Redaktion von DevJobs.at war das Spannende daran nicht nur die saubere technische Umsetzung, sondern vor allem die pragmatische Entscheidungslogik: Wann lohnt sich eine „klassische“ SPA mit dedizierter API – und wann ist ein Adapter wie Inertia.js der schnellere, schlankere Weg?
Bevor es in die Architektur ging, gab Jürgen einen Blick ins Produkt: jobs.at ist eine performante Jobplattform, deren Team mittlerweile auf über 20 Personen gewachsen ist. Standort ist Linz (mit Homeoffice-Flexibilität), ein zusätzlicher Standort in Wien ist in Vorbereitung. Fachlich relevant für diese Story: rund 70.000 Jobs im Bestand, etwa 830.000 Sessions pro Monat – und ein fairer, transparenter Pay-per-Click-Ansatz für Kunden statt fixer Anzeigenpreise ohne Leistungsbezug. Auf der technischen Seite setzt jobs.at im Backend auf PHP mit dem Laravel-Ökosystem und im Frontend vor allem auf Vue.js.
Diese Kombination bildet die Ausgangslage für die zentrale Frage des Talks: Wie lässt sich die UX einer SPA – schnell, app-ähnlich, interaktiv – in eine bestehende serverseitige Anwendung bringen, ohne die Komplexität und den Overhead eines vollwertigen API-Backends in Kauf zu nehmen?
SPAs und APIs: Gemeinsame Grundlage
Jürgen rollte die Begriffe zunächst sauber auf. Eine Single-Page Application (SPA) startet mit genau einer HTML-Seite als Einstiegspunkt – typischerweise ohne app-spezifische Inhalte. Der Client übernimmt die „heavy lifting“-Logik: Routing, Rendering, State. Daten fließen über JSON vom Server, klassisch via REST, inzwischen oft auch via GraphQL. Das Backend agiert dann vor allem als Datenlieferant (CRUD) und spricht mit dem Frontend über XHR und JSON, nicht über HTML-Templates.
Im Backend sind viele Stacks denkbar: Node.js, Laravel (PHP), sogar Backend-as-a-Service-Varianten wie Firebase. Im Frontend dominieren Frameworks wie Vue.js, React oder Angular. Diese Trennung ist inzwischen Standard – doch sie ist nicht alternativlos. Der Talk zeigte, dass sich das Spektrum zwischen „voll serverseitig gerendert“ und „voll clientseitig als SPA“ feiner abstufen lässt.
Warum SPAs? Vorteile – und für wen sie zählen
Jürgen benannte die klassischen Pro-Argumente:
- Kein vollständiger Page-Refresh: Teile der Seite können gezielt aktualisiert werden, was schnelle, fluide Interaktionen ermöglicht.
- App-ähnliche UX: Durch reaktives Rendering und optionale PWA-Features (Add to Home Screen, Offline-Support via Service Worker) entsteht ein „native-like“ Gefühl.
- Gute Developer Experience: Frontend-Frameworks bringen Struktur, Reaktivität und Skalierung mit sich – deutlich angenehmer als rohe DOM-Manipulation.
- Mehrere Clients auf derselben API: Ein Web-Frontend plus Mobile-App lässt sich mit einer gemeinsamen API bedienen.
- Backend wird schlicht: CRUD-Dienste, ggf. serverless (Lambda), weniger Server-Infrastruktur.
Diese Vorteile sind stark – aber nicht kostenlos.
Herausforderungen klassischer SPA+API-Setups
Die Kehrseite sprach Jürgen ebenso klar an:
- Zwei Repositories/Projekte: API und Frontend getrennt zu bauen und zu deployen, bedeutet zusätzliches Setup und laufenden Abstimmungsaufwand.
- Langsamerer Initial Load: Erster HTML-Load, dann JS, dann XHR für Daten – das kostet Zeit. SSR kann helfen, erhöht aber wiederum die Komplexität.
- Mehr HTTP-Overhead: Viele kleine Requests, häufigere Roundtrips.
- Build & Deployment werden aufwendiger: CI/CD muss für Frontend und API orchestriert werden.
- Authentifizierung & Sicherheit: Statt Sessions oft JSON Web Tokens, dazu CORS und Content Security Policy, ggf. Subdomains – alles machbar, aber zusätzliche Arbeit.
- SEO: Wenn Inhalte ausschließlich clientseitig gerendert werden, ist Indexierbarkeit nicht garantiert. Für öffentliche Seiten bleibt Server-Side-Rendering oft Pflicht.
Diese Punkte sind gut investierte Arbeit, wenn mehrere Clients bedient werden müssen – aber sie sind unnötige Last, wenn es „nur“ um eine Web-App innerhalb eines Monolithen geht.
Die Ausgangslage bei jobs.at: Laravel-SSR plus neues Nutzerprofil
Das Frontend von jobs.at war (und ist) eine klassische Laravel-App: serverseitig gerenderte Seiten mit Templates, optimiert auf Performance und Zugänglichkeit, ohne zusätzliche Third-Party-JavaScript-Frameworks. Authentifizierung per Session ist vorhanden, aber das Nutzerprofil war bis dato rudimentär: Login, Stammdaten, wenig mehr.
Das neue Projektziel: Das Nutzerprofil deutlich ausbauen – zunächst um Dokumentenverwaltung (CV, Zeugnisse) und deren Wiederverwendung im Bewerbungsprozess, mit Blick darauf, dass dieses Profil über Monate und Jahre wachsen wird. Zwei weitere Anforderungen prägten den Weg:
- Mobile-first: Die meisten Nutzer kommen mobil; der Upload/Verwaltungsflow muss unterwegs (Bus, Bahn) angenehm funktionieren.
- SEO irrelevant: Der Profilbereich liegt hinter Login – Suchmaschinenoptimierung spielt hier keine Rolle.
Kurz: Die UX sollte sich wie eine SPA anfühlen, aber die bestehende Laravel-App mit Session-Auth sollte bleiben. Massive Backend-Refactorings waren nicht gewünscht – Ressourcen sind endlich.
Zielbild: SPA-Erlebnis ohne API-Bruch
Aus dieser Lage entstand der Wunsch, Vue.js-Komponenten im Profilbereich „app-haft“ zu nutzen, aber zugleich bei Laravel-Routing, -Validierung und -Session zu bleiben. Statt eine REST/GraphQL-API abzukoppeln, sollte der Monolith leben – inklusive einfacher Integration in das bestehende Deployment.
Die Lösung fand das Team im Laravel-Ökosystem: Inertia.js.
Inertia.js: Adapter statt neues Framework
Inertia.js ist kein neues Frontend-Framework, sondern ein Adapter zwischen Backend und Frontend. Es erlaubt, Server-Routing (z. B. Laravel-Controller) beizubehalten und dennoch eine clientseitig erlebbare SPA zu bauen – mit Vue, React oder Svelte. Inertia übernimmt dabei das „Kleben“ zwischen Server-Antworten und Frontend-Komponenten, inklusive intelligenter Navigation, Props-Handling und Redirect-Verhalten.
Jürgen zitierte die eigene Selbstbeschreibung von Inertia: „building single-page apps without building an API“. Genau darum ging es im Projekt – das Monorepo behalten, Vue im Profilbereich einsetzen, ohne zusätzlich eine dedizierte API-Schicht zu etablieren.
So funktioniert der Inertia-Fluss
Jürgen erklärte den Request/Response-Flow schrittweise:
- Erster Seitenaufruf: Der Browser fordert eine HTML-Seite an; der Server liefert HTML zurück. Im DOM findet sich ein Root-Container (ein „Root-Div“), den Inertia nutzt. Diese Wurzel erhält ein
data-page-Attribut, das in JSON verschlüsselt, welche Vue-Komponente zu rendern ist und mit welchen Props. - Bootstrapping: Das Frontend lädt JavaScript; die Vue-App startet. Beim Booten liest Inertia das
data-page-Attribut, erkennt die gewünschte Komponente und rendert sie – die Seite „fühlt“ sich ab jetzt wie eine SPA. - Weitere Navigationen: Folgende Interaktionen laufen über XHR. Inertia setzt automatisch einen Header (X-Inertia), sodass Server und Client den speziellen Modus erkennen. Der Server antwortet nicht mit HTML-Templates, sondern mit einem JSON, das Komponente, Props, URL und optional Asset-Version (für Cache-Busting) enthält. Inertia tauscht basierend darauf die gerenderte Komponente aus – ohne Voll-Reload.
Wichtig: Auf Serverseite bleiben Controller-Logik und Datenzugriff wie in Laravel üblich. Der Unterschied liegt nur im Response-Typ: Statt eines Blade-Views geht eine Inertia-Response zurück, die die Props trägt. Auf Clientseite entfällt typischer XHR-Boilerplate (Axios-Promises, Error-Handling-Strukturen) – Inertia injiziert die Props direkt in die Zielkomponente.
Setup im Backend: Laravel bleibt Laravel
Der Adapter fügt sich in Laravel minimalinvasiv ein:
- Installation als Composer-Paket.
- Platzierung einer Inertia-Direktive im Basis-Layout an der Stelle, an der die SPA „leben“ soll. Diese Direktive rendert den Root-Container mit dem erwähnten
data-page-Attribut. - Controller bleiben – inklusive Routing, Validierung, Datenabfragen. Lediglich die Rückgabe erfolgt als Inertia-Response mit Name der Komponente und den Props.
- Middleware: Inertia bringt eine Middleware mit, die Besonderheiten behandelt, z. B. Redirects (Back-Navigation nach Formularfehlern), Flash-Data, Asset-Versionierung (ändert sich die gebaute Asset-Version, erzwingt Inertia einen Full-Reload, damit der Client die neuen Bundles lädt).
Das Ergebnis: Der gewohnte Laravel-Flow – nur dass am Ende statt HTML ein Inertia-Payload zurückgeht, der die Vue-Komponenten mit Daten füttert.
Setup im Frontend: Vue starten, Komponenten auflösen
Auf Clientseite wird der Inertia-Adapter als NPM-Paket installiert. Inertia unterstützt Vue, React und Svelte; für Angular ist laut Jürgen etwas in Arbeit. In Vue konfiguriert das Team die Inertia-App beim Booten und definiert eine „resolve“-Funktion: Bekommt Inertia den Komponentennamen aus dem Server-Response, weiß es anhand dieser Funktion, wo die entsprechende Vue-Datei liegt (z. B. im Verzeichnis für Profilseiten). Danach wird die App in das Root-Element gemountet.
Die einzelnen Komponenten sind klassische Vue-Komponenten: Template, Script, Props. Den Props-Namen entsprechen die Server-Keys, die der Controller übergibt. Das gewohnte „created“-Hook mit XHR-Datenladen entfällt – die Daten sind schon da. Das spart Code, reduziert Promise- und Fehlerbehandlung und beschleunigt den Entwicklungsfluss.
Formulare, Redirects und Flash-Messages
Ein wiederkehrender Fall in Profilbereichen sind Form-Submits. Jürgen zeigte den gewählten Umgang:
- Der Aufruf erfolgt über eine globale Inertia-Hilfe im Client (beispielsweise ein
post-Aufruf auf ein Endpoint mit dem Form-Datenobjekt). Optional lassen sich Callbacks wie „onStart“ und „onFinish“ nutzen, um Ladeindikatoren zu steuern. - Serverseitig wird in Laravel wie gewohnt umgeleitet – inklusive einer „with“-Nachricht (z. B. ein Erfolgshinweis). Die Inertia-Middleware erkennt den Kontext und verpackt diese Flash-Daten so, dass sie auf Clientseite als Teil der Inertia-Page-Daten verfügbar sind.
- In der Vue-Komponente greift man über eine globale „Page“-Referenz auf die Flash-Message zu und zeigt sie z. B. als Toast an.
Bemerkenswert dabei: Der gewohnte Laravel-Redirect-Flow bleibt intakt; Inertia sorgt „unter der Haube“ dafür, dass die Semantik einer SPA-Navigation gewahrt bleibt.
Authentifizierung, CORS, CSP: bewusst vermiedene Komplexität
Ein stiller, aber wesentlicher Gewinn des Inertia-Ansatzes liegt in all den Themen, die nicht umgesetzt werden mussten. Jürgen erinnerte daran, dass eine entkoppelte API oft neue Bausteine erfordert: JWT statt Session, CORS-Regeln für Subdomains, CSP-Anpassungen und eine Infrastruktur-Duplizierung. Da der Profilbereich in derselben Laravel-App bleibt, bleibt auch die Session-Authentifizierung bestehen. Für das Team hieß das: weniger Setup, weniger Fehlerquellen – und ein schnelles „Time to value“.
SEO-Überlegungen: richtig priorisiert
Weil der Profilbereich hinter Login liegt, ist SEO irrelevant. Jürgen betonte das, weil es eine wichtige Abzweigung markiert: Für öffentliche, indexierbare Seiten sollte man weiterhin SSR- oder Hybrid-Strategien erwägen. Für private, interaktive Bereiche darf das Pendel zugunsten einer clientseitigen UX ausschlagen – ohne die Komplexität einer separaten API.
Entwicklererfahrung: niedrige Einstiegshürde, wartbare Struktur
Jürgens Team-Feedback: Der Start mit Inertia fiel leicht. Es gibt keine neue „Megaschicht“, sondern nur wenige Dinge, die im Backend (Response-Typ, Middleware) und Frontend (Inertia-Bootstrap, Komponentenauflösung, globale Helfer) gelernt werden müssen. Die bestehende Laravel-Validierung, das Routing und die Controller-Muster bleiben unverändert – das ist Gold wert für Teams, die bereits solide Server-Apps pflegen.
Wann Inertia.js – und wann eine echte API?
Ein zentraler Takeaway aus „SPA without API“:
- Wenn die Web-App der einzige Client ist (und das absehbar bleibt), lohnt sich Inertia.js. Die Vorteile – schneller Start, weniger Infrastruktur, reibungsloser Dev-Flow – überwiegen.
- Wenn mehrere Clients bedient werden sollen (zusätzlich Mobile-App, Voice-Skill etc.), führt kein Weg an einer „echten“ API vorbei. Inertia ist für den Web-Client optimiert und nicht gedacht als universeller Multi-Client-Backbone.
Jürgen formulierte es pointiert: Es ist keine Schwarz-Weiß-Entscheidung. Adapter wie Inertia öffnen eine sinnvolle Mitte.
Praktische Hinweise aus dem Projekt
Aus dem Talk lassen sich mehrere, direkt anwendbare Hinweise abstrahieren:
- Bestehende Server-Routen weiterverwenden: Laravel-Controller bleiben der Taktgeber. Entwickelt den Profilbereich wie gewohnt – nur die Antwortart wechselt.
- Komponenten klar strukturieren: Der Komponentename ist die Brücke zwischen Server-Response und Vue-Datei. Eine klare Ordnerstruktur („Pages“, „Profile“) macht das Mapping robust.
- Props bewusst designen: Was die Controller zurückgeben, wird in den Props sichtbar. Saubere, flache Props halten die Komponenten schlank.
- Validierungsfehler übergeben: Der gewohnte Laravel-Mechanismus (Fehler im Session-Flash) entfaltet mit Inertia Wirkung – die Middleware speist Fehler und Messages sauber in die Page-Daten ein.
- Asset-Versionierung ernst nehmen: Inertia nutzt die Version zum Cache-Busting. Stellt sicher, dass eure Build-Pipeline diese Version konsistent aktualisiert.
- Mobile UX früh testen: Gerade bei Upload- und Formularflows zahlt sich eine feine Interaktionsgestaltung aus (Ladeindikatoren über die Inertia-Callbacks, sinnvolle Teilspeicherungen, klare Rückmeldungen).
- Grenzen kennen: Für öffentliche SEO-Seiten oder Multi-Client-Ökosysteme ist ein anderer Architekturpfad nötig (SSR/Hydration oder klassische API). Nutzt Inertia dort, wo es wirklich passt.
Fazit: „SPA without API“ als praktikable Mitte
„SPA without API“ von Jürgen Ratzenböck (jobs.at Recruiting GmbH) zeigt eine wohldosierte Architekturentscheidung: eine moderne, reaktive UX mit Vue, ohne eine komplette API-Landschaft aufzubauen. Die Kombination aus Laravel-SSR im breiten, öffentlichen Bereich und Inertia-getriebenem SPA-Feeling im privaten Profilbereich trifft die realen Bedürfnisse des Produkts – mobil optimiert, wartbar, schnell lieferbar.
Die Quintessenz für Engineering-Teams:
- Prüft, ob ihr wirklich eine dedizierte API braucht – oder ob ein Inertia-Ansatz im Monolithen eure Ziele schneller und robuster erfüllt.
- Haltet eure Entscheidungen eng an die Produktanforderungen: SEO ja/nein, Anzahl der Clients, Teamkompetenzen, Time-to-Market.
- Nutzt Stärken eurer bestehenden Plattform (Routing, Validierung, Sessions), statt sie vorschnell zu ersetzen.
Oder in den Worten der Inertia-Selbstbeschreibung, auf die Jürgen verwies: „building single-page apps without building an API“. Genau das hat jobs.at im Profilbereich umgesetzt – und genau das macht diese Lösung so anwendbar für viele Teams mit ähnlichen Rahmenbedingungen.