Häufig wird die UI unseren Jakarta EE Anwendungen als Single-Page-Application (SPA) realisiert. Beim Build kann uns z.B. das frontend-maven-plugin helfen, aber bei der Entwicklung verwenden wir häufig separate Entwicklungsserver und eine Proxy-Konfiguration. Quinoa integriert die SPA zur Entwicklungs- und Laufzeit mit wenig Konfigurationsaufwand in unsere Quarkus-Anwendung und bringt optional eine eigene NodeJS Installation mit.
Quinoa ist eine Quarkus Extension und kann zunächst ganz einfach als Dependency in unser Maven-Projekt hinzugefügt werden.
<dependency>
<groupId>io.quarkiverse.quinoa</groupId>
<artifactId>quarkus-quinoa</artifactId>
<version>2.6.2</version>
</dependency>
Standardmäßig erwartet Quinoa die Web-Anwendung im Projektpfad src/main/webui, der Pfad ist aber konfigurierbar und kann auch außerhalb des Maven-Projekts liegen. Wir schauen uns im folgenden als Beispiel eine kleine Anwendung an, die Informationen über die Planeten unseres Sonnensystems darstellt.
Die Backend-Anwendung ist in diesem Fall sehr simpel gehalten und liefert nur zwei REST-Endpunkte um alle eingetragenen Planeten auszugeben. Die REST-Schnittstellen sind mit quarkus.rest.path konfiguriert unter dem Pfad /api/* erreichbar.
Das Web-Frontend ist eine Angular-Anwendung, die einfach mit NPM installiert, gestartet und gebaut werden kann. Wenn lokal keine NodeJS oder NPM Installation vorliegt, kann Quinoa konfiguriert werden eine entsprechende Installation bereitzustellen und zu nutzen.
quarkus.quinoa.package-manager-install=true
quarkus.quinoa.package-manager-install.node-version=22.20.0
quarkus.quinoa.package-manager-install.npm-version=11.6.2
Ein Vorteil von Quinoa ist nun die Bereitstellung der Frontend-Anwendung im Quarkus Dev-Modus. Wenn wir die Anwendung zur Entwicklung mit mvn quarkus:dev starten wird automatisch auch ein Entwicklungsserver für Bereitstellung der Frontend-Anwendung gestartet. Wenn wir auf die Quarkus-Anwendung zugreifen erreichen wir aber nun, anders als mit dem frontend-maven-plugin, unter http://localhost:8080/ keine statische gebaute Variante der Frontend-Anwendung. Die Anfragen werden von Quinoa an den Entwicklungsserver weitergeleitet und wir haben dieselben Live-Coding Vorteile für Front- und Backend-Anteile.
Wenn wir Front- und Backend wie in diesem Fall zusammen ausliefern gibt es noch das Problem, dass die Single-Page-Application nur unter index.html aufgerufen wird. Versuchen wir nun direkt über den Adresspfad auf Frontend-Routen zuzugreifen werden diese vom Backend nicht gefunden und wir bekommen eine Fehlermeldung. Üblicherweise definieren wir für diesen Fall eine Weiterleitung von Pfaden die vom Backend nicht behandelt werden, aber auch hier kann uns Quinoa behilflich sein. Mit der Einstellung quarkus.quinoa.enable-spa-routing=true weisen wir Quinoa an eine entsprechende Behandlung der Routen zu aktivieren.
Die Anwendung läuft nicht nur im Dev-Modus, sondern kann auch ganz normal über Maven gebaut werden. Dabei wird ein Build der Angular Anwendung von Quinoa an der passenden Stelle eingefügt um die gebaute SPA beim Paketieren mit zu berücksichtigen und als Ressource einzufügen.
Für den Zugriff auf die im Backend definierten REST-Ressourcen im Frontend nutzen wir TypeScript-Services, die auf Basis einer OpenAPI-Definition automatisch generiert werden. Die OpenAPI-Definition selbst wird beim Bauen des Backends von der quarkus-smallrye-openapi Extension auf Basis der in der Anwendung definierten (kompilierten) REST-Ressourcen erstellt. Hier muss also zunächst das Backend kompiliert, dann die OpenAPI Dokumentation erstellt werden um daraufhin vor dem Build der Angular-Anwendung die Schnittstellen-Services generieren zu können. Das Paketieren der Anwendung darf allerdings erst geschehen, wenn auch das Frontend gebaut ist um die Ressourcen an entsprechender Stelle einzufügen. Hier sorgt Quinoa standardmäßig bereits dafür, dass der Frontend-Build an der richtigen Stelle eingefügt wird und die OpenAPI-Definition vorher bereit steht.
Das Beispiel gibt es zum Ausprobieren natürlich wie immer auch auf GitHub.






