-
Notifications
You must be signed in to change notification settings - Fork 0
QuickStart react
Setting up and running a Hilla application on Quarkus using quarkus-hilla-react
extension.
Following this guide you will create a simple Todo List application using Hilla and Quarkus, using React for front-end development.
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 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 \
-Dextensions=quarkus-hilla-react
On the command line we specified that the new project will use the quarkus-hilla-react
extension.
The Quarkus maven plugin will create a new project for an example Hilla application.
Note
|
The following commands will use the maven wrapper. |
Once the project is created, enter the getting-started
directory and add the other required extensions to the project:
./mvnw quarkus:add-extensions \
-Dextensions="quarkus-hibernate-orm-panache, \
quarkus-jdbc-h2, \
quarkus-hibernate-validator"
The POM file should now contain the quarkus-hilla-react
dependency, the Hilla BOM and the configuration of the hilla-maven-plugin
:
<project>
<dependencies>
<dependency>
<groupId>com.github.mcollovati</groupId>
<artifactId>quarkus-hilla-react</artifactId>
<version>2.4.0</version>
</dependency>
</dependencies>
</project>
<project>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>dev.hilla</groupId>
<artifactId>hilla-bom</artifactId>
<version>${hilla.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
<project>
<build>
<plugins>
<plugin>
<groupId>dev.hilla</groupId>
<artifactId>hilla-maven-plugin</artifactId>
<version>${hilla.version}</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, update the value in the |
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 \
-Dextensions="quarkus-hibernate-orm-panache, \
quarkus-jdbc-h2, \
quarkus-hibernate-validator, \
quarkus-hilla-react"
You can now remove the unnecessary generated examples files.
Delete all the contents inside the src/main/java/com/example/application
folder,
and the MainView.tsx
file in fronted/views
folder.
Next, 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, edit 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.BrowserCallable;
import dev.hilla.Nonnull;
import com.vaadin.flow.server.auth.AnonymousAllowed;
@BrowserCallable
@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 @BrowserCallable
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 TodoView.tsx
in the frontend/views
directory, with the following content:
import {useEffect, useState} from "react";
import {useForm} from '@hilla/react-form';
import {Button} from "@hilla/react-components/Button.js";
import {TextField} from "@hilla/react-components/TextField.js";
import {Checkbox} from "@hilla/react-components/Checkbox.js";
import Todo from "Frontend/generated/com/example/application/Todo";
import {TodoEndpoint} from "Frontend/generated/endpoints.js";
import TodoModel from "Frontend/generated/com/example/application/TodoModel";
export default function TodoView() {
const [todos, setTodos] = useState<Todo[]>([]);
const {model, field, invalid, dirty, submit, clear} = useForm(TodoModel, {
onSubmit: async (todo) => {
todo.id = NaN;
const newTodo = await TodoEndpoint.create(todo);
setTodos([...todos, newTodo]);
clear();
}
});
const updateTodoState = async (todo: Todo, done: boolean) => {
if (todo.done !== done) {
todo.done = done;
const updatedTodo = await TodoEndpoint.update(todo);
setTodos(todos.map(t => t.id === updatedTodo.id ? updatedTodo : t));
}
}
useEffect(() => {
TodoEndpoint.findAll().then(setTodos);
}, []);
return (
<>
<div className={'form'}>
<TextField
label="Task"
{...field(model.task)}
/>
<Button onClick={submit} disabled={!dirty || invalid} theme={'primary'}>Add</Button>
</div>
<div className={'todos'}>
{todos.map(todo => (
<div className={"todo"} key={todo.id}>
<Checkbox checked={todo.done}
onCheckedChanged={(event) => updateTodoState(todo, event.detail.value)}
label={todo.task}></Checkbox>
</div>
))}
</div>
</>
);
}
In the frontend
directory edit the routes.tsx
file, to configure the React router in order to show the Todo view.
import TodoView from "Frontend/views/TodoView.js";
import {
createBrowserRouter,
RouteObject
} from "react-router-dom";
export const routes: readonly RouteObject[] = [
{ path: "/", element: <TodoView /> },
];
export const router = createBrowserRouter([...routes], {basename: new URL(document.baseURI).pathname });
Restart the server, open a browser and navigate to http://localhost:8080.
You should now have a working Todo application.
The project is already configure with a production
profile 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>${hilla.version}</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