Skip to content

Commit

Permalink
Introduce Quarkus for the Web documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
ia3andy committed Apr 9, 2024
1 parent f1faf27 commit 3122557
Show file tree
Hide file tree
Showing 4 changed files with 330 additions and 25 deletions.
31 changes: 31 additions & 0 deletions docs/src/main/asciidoc/http-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,44 @@ In its absence, RESTEasy operates directly on Vert.x without involving Servlets.

== Serving Static Resources

If you are looking to use Quarkus for your Web Application, you might want to check the xref:web.adoc[Quarkus for the Web] guide.

=== From the application jar

To serve static resources from the application jar, you must place them in the `META-INF/resources` directory of your application. This location
was chosen as it is the standard location for resources in `jar` files as defined by the Servlet spec. Even though
Quarkus can be used without Servlet, following this convention allows existing code that places its resources in this
location to function correctly.

[[from-mvnpm]]
=== From MVNPM

If you are using https://mvnpm.org/[mvnpm.org], like the following JQuery one:

[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
.pom.xml
----
<dependency>
<groupId>org.mvnpm</groupId>
<artifactId>jquery</artifactId>
<version>3.7.1</version>
</dependency>
----

[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"]
.build.gradle
----
implementation("org.mvnpm:jquery:3.7.1")
----

You can use it in your html like this:
[source,html]
----
<script src="/_static/jquery/3.7.1/dist/jquery.min.js"></script>
----


[[from-webjars]]
=== From WebJars

If you are using webjars, like the following JQuery one:
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 26 additions & 25 deletions docs/src/main/asciidoc/qute.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -26,45 +26,48 @@ Clone the Git repository: `git clone {quickstarts-clone-url}`, or download an {q

The solution is located in the `qute-quickstart` link:{quickstarts-tree-url}/qute-quickstart[directory].

== Hello World with Jakarta REST

If you want to use Qute in your Jakarta REST application, you need to add an extension first:
[[hello-qute-web]]
== Hello World with Qute Web

* either `quarkus-rest-qute` if you are using Quarkus REST (formerly RESTEasy Reactive):
+
If you want to use Qute in your Quarkus Web application, add the Qute Web extension first:
[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
.pom.xml
----
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-qute</artifactId>
<groupId>io.quarkiverse.qute.web</groupId>
<artifactId>quarkus-qute-web</artifactId>
</dependency>
----
+
[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"]
.build.gradle
----
implementation("io.quarkus:quarkus-rest-qute")
implementation("io.quarkiverse.qute.web:quarkus-qute-web")
----

* or `quarkus-resteasy-qute` if you are using RESTEasy Classic:
+
[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
.pom.xml
----
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-qute</artifactId>
</dependency>
----
+
[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"]
.build.gradle
All files located in the `src/main/resources/templates` directory and its subdirectories are registered as templates. Templates are validated during startup and watched for changes in the development mode.

Now, let's start with a Hello World html template:

.src/main/resources/templates/pub/hello.html
[source]
----
implementation("io.quarkus:quarkus-resteasy-qute")
<h1>Hello {http:param('name', 'Quarkus')}!</h1> <1>
----
<1> `{http:param('name', 'Quarkus')}` is an expression that is evaluated when the template is rendered (Quarkus is the default value).

We'll start with a very simple template:
NOTE: Templates located in the `pub` directory are served via HTTP. Automatically, no controllers needed. For example, the template src/main/resource/templates/pub/foo.html will be served from the paths /foo and /foo.html by default.

If your application is running, you can open your browser and hit: http://localhost:8080/hello?name=Martin

For more information about Qute Web, see the https://docs.quarkiverse.io/quarkus-qute-web/dev/index.html[Qute Web guide].

[[hello-qute-rest]]
== Hello World with Jakarta REST

If you want to use Qute in your Jakarta REST application, you still need to add the Qute Web extension first (see <<hello-qute-web>>) and make sure you have the Quarkus REST (formerly RESTEasy Reactive) extension.

Here is a very simple text template:

.hello.txt
[source]
Expand All @@ -73,8 +76,6 @@ Hello {name}! <1>
----
<1> `{name}` is a value expression that is evaluated when the template is rendered.

NOTE: By default, all files located in the `src/main/resources/templates` directory and its subdirectories are registered as templates. Templates are validated during startup and watched for changes in the development mode.

Now let's inject the "compiled" template in the resource class.

.HelloResource.java
Expand Down
273 changes: 273 additions & 0 deletions docs/src/main/asciidoc/web.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
////
This guide is maintained in the main Quarkus repository
and pull requests should be submitted there:
https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc
////
= Quarkus for the Web
include::_attributes.adoc[]
:categories: web
:summary: Learn more about creating all kinds of Web applications with Quarkus.
:numbered:
:sectnums:
:sectnumlevels: 3
:topics: http,web,renarde,full-stack,qute,quinoa,web-bundler,mvc,ssr,nodejs,npm,javascript,css,jsf
:extensions: o.quarkiverse.qute.web:quarkus-qute-web,io.quarkiverse.renarde:quarkus-renarde,io.quarkiverse.web-bundler:quarkus-web-bundler,io.quarkiverse.quinoa:quarkus-quinoa

Quarkus provides different extensions to create web applications, this document aims to provide directions on which extension to use for different use cases.

== The basics

=== Serving Static Resources

Let's assume you have a Quarkus backend, and you want to serve static files. This is the most basic case, it is supported out of the box with all our Vert.x based extensions, you must place them in the `META-INF/resources` directory of your application.

You can find more information in xref:http-reference#serving-static-resources[The HTTP Reference].

=== Serving Scripts, Styles, and web libraries

However, if you want to insert scripts, styles, and libraries in your web pages, you have 3 options:

a. Consume libraries from public CDNs such as cdnjs, unpkg, jsDelivr and more, or copy them to your `META-INF/resources` directory.
b. Use runtime web dependencies such as mvnpm.org or webjars, when added to your pom.xml or build.gradle they can be directly xref:http-reference#from-mvnpm[accessed from your web pages].
c. Package your scripts (js, ts), styles (css, scss), and web dependencies together using a bundler (see xref:#bundling[below]).

NOTE: *We recommend using a bundler for production* as it offers better control, consistency, security, and performance. The good news is that Quarkus makes it really easy and fast with the https://docs.quarkiverse.io/quarkus-web-bundler/dev/[Quarkus Web Bundler extension].

[[bundling]]
=== Bundling Scripts, Styles, and Libraries

There are two ways to bundle your web assets:

a. Using https://docs.quarkiverse.io/quarkus-web-bundler/dev/[the Quarkus Web Bundler extension], which is the recommended way. Without any configuration, it puts everything together in an instant, and follows good practices such as dead-code elimination, minification, caching, and more.
b. Using a custom bundler such as Webpack, Parcel, Rollup, etc. This can be easily integrated with Quarkus using the https://quarkiverse.github.io/quarkiverse-docs/quarkus-quinoa/dev/[Quarkus Quinoa extension].

image::web-bundle-transition.png[Web Bundle Transition]

== Server-side rendering (SSR)

For templating and server-side rendering with Quarkus, there are different engines available such as xref:qute.adoc[Qute] or https://docs.quarkiverse.io/quarkus-freemarker/dev/[Freemarker] and others.

=== Qute Web

Qute is designed specifically to meet the Quarkus needs, and help you deal with templates, snippets, and partials and render the data from your storage. It is inspired by the most famous template engines, it is fast, type-safe, works in native, and has a lot of nice features.

To install Qute Web, follow xref:qute.adoc[the instructions].

Here is a simple example of a Qute template:

.src/main/resources/templates/pub/index.html
[source,html]
----
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Qute Page</title>
{#bundle /} <1>
</head>
<body>
<h1>Hello {http:param('name', 'Quarkus')}</h1> <2>
<ul>
{#for item in cdi:Product.items} <3>
<li>{item.name} {#if item.active}{item.price}{/if}</li> <4>
{/for}
</ul>
</body>
</html>
----

<1> With the https://docs.quarkiverse.io/quarkus-web-bundler/dev/[Web Bundler extension], this expression will be replaced by the bundled scripts and styles.
<2> You can directly use the HTTP parameters in your templates.
<3> This expression is validated. Try to change the expression to `cdi:Product.notHere` and the build should fail.
<4> If you install xref:ide-tooling.adoc[Quarkus IDEs plugins], you will have autocompletion, link to implementation and validation.

=== Model-View-Controller (MVC)

The MVC approach is also made very easy with Quarkus thanks to https://docs.quarkiverse.io/quarkus-renarde/dev/index.html[the Renarde extension], a Rails-like framework using Qute.

Associated with the https://docs.quarkiverse.io/quarkus-web-bundler/dev/[Web Bundler extension], the road is open to build modern web applications for all you needs. Here is what a simple Renarde controller looks like:

.src/main/java/rest/Todos.java
[source,java]
----
package rest;
[...]
public class Todos extends Controller {
@CheckedTemplate
static class Templates {
public static native TemplateInstance index(List<Todo> todos);
}
public TemplateInstance index() {
// list every todo
List<Todo> todos = Todo.listAll();
// render the index template
return Templates.index(todos);
}
@POST
public void add(@NotBlank @RestForm String task) {
// check if there are validation issues
if(validationFailed()) {
// go back to the index page
index();
}
// create a new Todo
Todo todo = new Todo();
todo.task = task;
todo.persist();
// send loving message
flash("message", "Task added");
// redirect to index page
index();
}
@POST
public void delete(@RestPath Long id) {
// find the Todo
Todo todo = Todo.findById(id);
notFoundIfNull(todo);
// delete it
todo.delete();
// send loving message
flash("message", "Task deleted");
// redirect to index page
index();
}
@POST
public void done(@RestPath Long id) {
// find the Todo
Todo todo = Todo.findById(id);
notFoundIfNull(todo);
// switch its done state
todo.done = !todo.done;
if(todo.done)
todo.doneDate = new Date();
// send loving message
flash("message", "Task updated");
// redirect to index page
index();
}
}
----

== Single Page Applications

Quarkus provides very solid tools for creating or integrating Single Page Applications to Quarkus (React, Angular, Vue, …), here are 3 options:

* https://quarkiverse.github.io/quarkiverse-docs/quarkus-quinoa/dev/[Quarkus Quinoa] bridges your npm-compatible web application and Quarkus for both dev and prod. No need to install Node.js or configure your framework, it will detect it and use sensible defaults.
* The https://docs.quarkiverse.io/quarkus-web-bundler/dev/[Quarkus Web Bundler] is also a good approach, it is closer to the Java ecosystem and removes a lot of boilerplate and configuration, it is fast and efficient. For examples of such SPAs, see https://github.com/quarkusio/code.quarkus.io[code.quarkus.io] and https://github.com/mvnpm/mvnpm[mvnpm.org].
* Your automation using the https://github.com/eirslett/frontend-maven-plugin[maven-frontend-plugin] or similar tools.

== Full-stack microservices (Micro-frontends)

Quarkus is an excellent choice for both full-stack web components and full-stack microservices aka Micro-frontends. By utilizing the Web Bundler or Quinoa, you can significantly reduce boilerplate code and manage multiple services efficiently without much configuration duplication.

For example the https://github.com/quarkusio/search.quarkus.io[Guide search] on https://quarkus.io[quarkus.io] uses the Web Bundler to create a full-stack web-component. With Lit Element for the web-component and OpenSearch for the indexation it is a nice way to enhance the static web-site experience in a dynamic way.

More content about this is coming soon...
// Blog article in prep: https://github.com/quarkusio/quarkusio.github.io/issues/1934

== Other ways

We described Quarkus most common ways to create web applications but there are other options:

* Vaadin Flow is a unique framework that lets you build web apps directly from Java code without writing HTML or JavaScript
* JavaServer Faces is a specification for building component-based web apps in Java. There are 3 extensions to help with JSF in Quarkus: MyFaces, OmniFaces, PrimeFaces.
// 👆 Blog article in prep https://github.com/quarkusio/quarkusio.github.io/issues/1935
* Create xref:building-my-first-extension.adoc[a new extension] for your favorite web framework

== Testing your web applications

For testing web applications, https://docs.quarkiverse.io/quarkus-playwright/dev/[Quarkus Playwright] is very easy to use. You can create effective cross-browser end-to-end tests mimicking user interaction and making sure your web application is working as a whole. The big advantage is that it benefits from all dev-services and Quarkus mocking features.

[source,java]
----
@QuarkusTest
@WithPlaywright
public class WebApplicationTest {
@InjectPlaywright
BrowserContext context;
@TestHTTPResource("/")
URL index;
@Test
public void testIndex() {
final Page page = context.newPage();
Response response = page.navigate(index.toString());
Assertions.assertEquals("OK", response.statusText());
page.waitForLoadState();
String title = page.title();
Assertions.assertEquals("My Awesome App", title);
// Make sure the web app is loaded and hits the backend
final ElementHandle quinoaEl = page.waitForSelector(".toast-body.received");
String greeting = quinoaEl.innerText();
Assertions.assertEquals("Hello from REST", greeting);
}
}
----

== Q&A

=== Why is Quarkus a very good option for Web Applications compared to other frameworks?

Quarkus is well known for its backend extensions ecosystem and developer experience, if you combine it with great extensions for frontend, then it is a perfect mix! All the testing and dev-mode features are now available for both frontend and backend.

=== What are the advantages of SSR (Server Side Rendering) over SPA (Single Page App)?
Here are the benefits of performing rendering work on the server:

*Data Retrieval:* Fetching data on the server, closer to the data source. This enhances performance by reducing the time needed to retrieve data for rendering and minimizes client requests.

*Enhanced Security:* Storage of sensitive data and logic is happening on the server, such as tokens and API keys, without exposing them to client-side risks.

*Caching Efficiency:* Server-side rendering allows for result caching, which can be reused across users and subsequent requests. This optimizes performance and lowers costs by reducing rendering and data fetching per request.

*Improved Initial Page Load and First Contentful Paint (FCP):* Generating HTML on the server enables users to view the page immediately, eliminating the need to wait for client-side JavaScript to download, parse, and execute for rendering.

*Search Engine Optimization (SEO) and Social Media Shareability:* The rendered HTML aids search engine indexing and social network previews, enhancing discoverability and shareability.


=== I am hesitating between Quinoa and the Web Bundler, how should I make my decision?

You have to think that the bundled output is essentially the same with both solutions. Also, switching from one to the other is not a big deal, the choice is about the developer experience and finding the best fit for your team.

Some guidelines:

*Go for Quinoa:*

* You have an existing frontend configured with a npm-compatible build tool, Quinoa is the most direct option.
* You have a dedicated frontend team familiar with tools such as NPM, Yarn and other for building Single Page Apps.
* If you want to write Javascript unit tests (such as Jest, Jasmine, ..), it is not possible with the Web Bundler. However, you could publish a components library on NPM and consume it from the Web Bundler.
* If you use very specific bundling options or specific tools in your build process
* If you love package.json and configurations tweaking

*Go for Web Bundler:*

* For simple web applications, the Web Bundler is the easiest and fastest way to get started
* If you prefer to stay close to the Maven/Gradle ecosystem
(Node.js is not needed), it uses an extremely fast bundler for the web (esbuild)
* If you want to reduce boilerplate and configuration


=== How do I scale a Quarkus Web Application?

Serving a few static pages and scripts from an existing Quarkus backend is not a big overhead, so scaling the full app is usually the simplest option.
You could also split it in two services: one for the backend and one for the frontend. However, in most cases, this approach wouldn’t yield substantial benefits compared to the initial method.

If your application involves a substantial number of static resources, consider using a CDN. Both the Web-Bundler and Quinoa can be configured to work seamlessly with a CDN, providing improved performance and distribution of assets.

It would be nice to have a blog article and benchmark about this topic, please open an issue if you are interested in writing it.





0 comments on commit 3122557

Please sign in to comment.