-
Notifications
You must be signed in to change notification settings - Fork 0
QuickStart
Setting up and running a Hilla application on Quarkus using quarkus-hilla
extension.
Following this guide you will create a simple Todo List application using Hilla and Quarkus, using Lit for front-end devlopment.
The application will use Panache to persist items in an in-memory H2 database.
Note
|
This guide is for Quarkus Hilla 2.x. For Quarkus Hilla 1.x see this guide. |
You can create a new empty Quarkus project using Maven from the command line:
mvn io.quarkus.platform:quarkus-maven-plugin:3.5.2:create \
-DprojectGroupId=com.example.application \
-DprojectArtifactId=getting-started \
-DplatformVersion=3.5.2
Note
|
The following commands will use the maven wrapper,
because there is an issue with Maven 3.9.0+ and Quarkus versions below 3.0. Check the Quarkus issue for further information. |
Once the project is created, enter the getting-started
directory and add the required extensions to the project:
./mvnw quarkus:add-extensions \
-Dextensions="quarkus-hibernate-orm-panache, \
quarkus-jdbc-h2, \
quarkus-hibernate-validator"
To add the quarkus-hilla
extension, you need to specify the full coordinates and version, because the extension is not yet published in the
Quarkus Registry.
./mvnw quarkus:add-extension -Dextension=com.github.mcollovati:quarkus-hilla:2.4.0
Or add the dependency manually:
<dependency>
<groupId>com.github.mcollovati</groupId>
<artifactId>quarkus-hilla</artifactId>
<version>2.4.0</version>
</dependency>
And this is the all-in-one statement to create the project and add the extensions:
mvn io.quarkus.platform:quarkus-maven-plugin:3.5.2:create \
-DprojectGroupId=com.example.application \
-DprojectArtifactId=getting-started \
-DplatformVersion=3.5.2 \
-Dextensions="quarkus-hibernate-orm-panache, \
quarkus-jdbc-h2, \
quarkus-hibernate-validator, \
com.github.mcollovati:quarkus-hilla:2.4.0"
Once the extension are added, you need to configure the hilla-maven-plugin
.
Add the plugin definition to the build → plugins
section of the POM file.
<project>
<build>
<plugins>
<plugin>
<groupId>dev.hilla</groupId>
<artifactId>hilla-maven-plugin</artifactId>
<version>2.4.0</version>
<executions>
<execution>
<goals>
<goal>prepare-frontend</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Note
|
If you want to use a specific Hilla version, add the <project>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>dev.hilla</groupId>
<artifactId>hilla-bom</artifactId>
<version>2.4.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project> |
As a last step, you should configure the persistence settings in application.properties
quarkus.datasource.db-kind=h2
quarkus.datasource.jdbc.url=jdbc:h2:mem:rest-crud
quarkus.datasource.jdbc.driver=org.h2.Driver
quarkus.datasource.jdbc.max-size=8
quarkus.datasource.jdbc.min-size=2
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.log.sql=true
To add some sample data, create the file import.sql
in src/main/resources/
with the following content:
INSERT INTO todo(id, task, done) VALUES (nextval('Todo_SEQ'), 'Introduction to Quarkus', true);
INSERT INTO todo(id, task, done) VALUES (nextval('Todo_SEQ'), 'Hibernate with Panache', false);
INSERT INTO todo(id, task, done) VALUES (nextval('Todo_SEQ'), 'Visit Quarkus website', false);
INSERT INTO todo(id, task, done) VALUES (nextval('Todo_SEQ'), 'Start Quarkus project', false);
Now that the project is set up, you can start coding the application.
The Todo item will be modeled as a JPA entity class. Create the file Todo.java
in src/main/resources/com/example/application
with the following content:
package com.example.application;
import jakarta.persistence.Entity;
import jakarta.validation.constraints.NotBlank;
import io.quarkus.hibernate.orm.panache.PanacheEntity;
@Entity
public class Todo extends PanacheEntity {
private boolean done = false;
@NotBlank
private String task;
public Todo() {
}
public Todo(String task) {
this.task = task;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public boolean isDone() {
return done;
}
public void setDone(boolean done) {
this.done = done;
}
public String getTask() {
return task;
}
public void setTask(String task) {
this.task = task;
}
}
Create a repository class to access the database. By extending PanacheRepository
you will get the methods for the most common persistence operations.
Create the TodoRepository.java
file in src/main/resources/com/example/application
with the following content:
package com.example.application;
import jakarta.enterprise.context.ApplicationScoped;
import io.quarkus.hibernate.orm.panache.PanacheRepository;
@ApplicationScoped
public class TodoRepository implements PanacheRepository<Todo> {
}
To be able to invoke server side operations from the frontend, you will use a Hilla endpoint. Endpoints are annotated classes, for which Hilla is able to generate a TypeScript interface to be used in the front-end code.
Create a new TodoEndpoint.java
file in src/main/java/com/example/application
with the following content:
package com.example.application;
import jakarta.transaction.Transactional;
import jakarta.validation.Valid;
import java.util.List;
import dev.hilla.Endpoint;
import dev.hilla.Nonnull;
import com.vaadin.flow.server.auth.AnonymousAllowed;
@Endpoint
@AnonymousAllowed
public class TodoEndpoint {
private TodoRepository repository;
public TodoEndpoint(TodoRepository repository) {
this.repository = repository;
}
public @Nonnull List<@Nonnull Todo> findAll() {
return repository.listAll();
}
@Transactional
public Todo create(@Valid Todo todo) {
repository.persist(todo);
return todo;
}
@Transactional
public Todo update(@Valid Todo todo) {
Todo entity = repository.findById(todo.getId());
entity.setDone(todo.isDone());
entity.setTask(todo.getTask());
return entity;
}
}
The @Endpoint
annotation marks the class as a Hilla endpoint;
@AnonymousAllowed
means that methods can be accessed by not authenticated users.
@Transactional
is required for operations that modify the database.
See the Hilla documentation for further information.
Now, start the application, for example by typing mvn quarkus:dev
on the
command line, and let Hilla generate the TypeScript code for you.
Create a new file todo-view.ts
in the frontend
directory, with the following content:
import { html, LitElement } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import '@vaadin/button';
import '@vaadin/checkbox';
import '@vaadin/text-field';
import { Binder, field } from '@hilla/form';
import Todo from 'Frontend/generated/com/example/application/Todo';
import TodoModel from 'Frontend/generated/com/example/application/TodoModel';
import { TodoEndpoint } from 'Frontend/generated/endpoints';
@customElement('todo-view')
export class TodoView extends LitElement {
@state()
private todos: Todo[] = [];
private binder = new Binder(this, TodoModel);
render() {
return html`
<div class="form">
<vaadin-text-field label="Task" ${field(this.binder.model.task)}></vaadin-text-field>
<vaadin-button theme="primary" @click=${this.createTodo} ?disabled=${this.binder.invalid}>
Add
</vaadin-button>
</div>
<div class="todos">
${this.todos.map(
(todo) => html`
<div class="todo">
<vaadin-checkbox
?checked=${todo.done}
@checked-changed=${(e: CustomEvent) => this.updateTodoState(todo, e.detail.value)}></vaadin-checkbox>
<span>${todo.task}</span>
</div>
`)}
</div>
`;
}
async connectedCallback() {
super.connectedCallback();
this.todos = await TodoEndpoint.findAll();
}
async createTodo() {
const createdTodo = await this.binder.submitTo(TodoEndpoint.create);
if (createdTodo) {
this.todos = [...this.todos, createdTodo];
this.binder.clear();
}
}
updateTodoState(todo: Todo, done: boolean) {
if (todo.done !== done) {
todo.done = done;
const updatedTodo = { ...todo };
this.todos = this.todos.map((t) => (t.id === todo.id ? updatedTodo : t));
TodoEndpoint.update(updatedTodo);
}
}
}
In the frontend
directory create an index.ts
file, to configure the Vaadin router
in order to show the Todo view.
import { Router } from '@vaadin/router';
import './todo-view'
import { color, typography } from "@vaadin/vaadin-lumo-styles/all-imports.js";
const style = document.createElement("style");
style.innerHTML = `${color.toString()} ${typography.toString()}`;
document.head.appendChild(style);
export const router = new Router(document.querySelector('#outlet'));
const routes = [
{
path: '',
component: 'todo-view',
},
]
router.setRoutes(routes);
Restart the server, open a browser and navigate to http://localhost:8080.
You should now have a working Todo application.
To create a production build add a profile to the POM file that triggers the
build-frontend
goal of the hilla-maven-plugin
.
<profile>
<!-- Production mode is activated using -Pproduction -->
<id>production</id>
<build>
<plugins>
<plugin>
<groupId>dev.hilla</groupId>
<artifactId>hilla-maven-plugin</artifactId>
<version>2.4.0</version>
<executions>
<execution>
<goals>
<goal>build-frontend</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
Now you can build the application with the following maven command
./mvnw -Pproduction package
And finally start the application by typing
java -jar target/quarkus-app/quarkus-run.jar
Complete source code can be found at https://github.com/mcollovati/quarkus-hilla-todo-example