Skip to content

QuickStart

Marco Collovati edited this page Dec 1, 2023 · 14 revisions

Quick start (Lit)

Setting up and running a Hilla application on Quarkus using quarkus-hilla extension.

Introduction

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.

Requirements

  • JDK 17 or later

  • Maven 3.8 or later

Creating a new Quarkus project

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"

Configure the project

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 hilla-bom to the dependencyManagement section of your pom.xml and remove the plugin version from the plugin definition.

<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);

Create the application

Now that the project is set up, you can start coding the application.

Define an entity

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

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> {
}

Create a Typed Server Endpoint

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.

Build the Todo View

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);

Run the application

Restart the server, open a browser and navigate to http://localhost:8080.

You should now have a working Todo application.

app

Build for production

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