Lab 1 – Hello Flow & Dev UI

In this lab you:

  • Create (or reuse) a Quarkus app.

  • Add the Quarkus Flow extension.

  • Write a minimal workflow using the Java DSL.

  • Invoke it via REST and inspect it in Quarkus Dev UI.

1. Create the Quarkus project

If you don’t have a project yet, use the Quarkus CLI:

quarkus create app org.acme:hello-flow \
  --extension=resteasy-reactive-jackson
cd hello-flow

Or add RESTEasy Reactive with JSON to an existing project.

Quarkus Flow has a Codestart available, which you can use to create a project with a sample workflow and JAX-RS endpoint.

As we are focusing on creating a minimal example here, we won’t use the Codestart in this lab.

Add the Quarkus Flow extension

Add the Quarkus Flow extension to your project:

./mvnw quarkus:add-extension -Dextensions="io.quarkiverse.flow:quarkus-flow"

Or add manually to your pom.xml:

<dependency>
    <groupId>io.quarkiverse.flow</groupId>
    <artifactId>quarkus-flow</artifactId>
    <version>{project-version}</version>
</dependency>

2. Create a minimal workflow

Create a Flow subclass that returns a simple greeting. For example:

package org.acme;

import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.set;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkiverse.flow.Flow;
import io.serverlessworkflow.api.types.Workflow;
import io.serverlessworkflow.fluent.func.FuncWorkflowBuilder;

@ApplicationScoped
public class HelloWorkflow extends Flow {
    @Override
    public Workflow descriptor() {
        return FuncWorkflowBuilder.workflow("hello")
                // jq expression to set our context to the JSON object `message`
                .tasks(set("{ message: \"hello world!\" }"))
                .build();
    }
}

This workflow builds a simple do sequence with one task that sets message = "hello world!" and returns it as the workflow output.

3. Expose it through REST

Create a REST resource that injects the workflow and starts an instance:

package org.acme;

import java.util.Map;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

import org.jboss.resteasy.reactive.ResponseStatus;

import io.smallrye.mutiny.Uni;

@Path("/hello")
@ApplicationScoped
public class HelloResource {

    @Inject
    HelloWorkflow hello; // inject the Flow subclass

    @GET
    @ResponseStatus(200)
    public Uni<Message> hello() {
        return hello
                .startInstance(Map.of()) // convenience on Flow
                .onItem()
                .transform(w -> w.as(Message.class).orElseThrow());
    }
}

We need also the Message class, a simple wrapper for a message.

package org.acme;

public record Message(String message) {
}

Key points:

  • The resource injects the HelloWorkflow class directly.

  • startInstance() creates a Uni<WorkflowModel>.

  • .onItem().transform(…​) observes the item emitted by Uni<WorkflowModel> and transforms it to Uni<Message>.

  • The method returns Uni<Message>, so the endpoint stays non-blocking.

4. Run in dev mode

Start Quarkus in dev mode:

./mvnw quarkus:dev

Test the endpoint:

curl http://localhost:8080/hello

You should see a JSON output similar to:

{"message":"hello world!"}

5. Explore Quarkus Dev UI

With dev mode still running, open the Dev UI in your browser:

Look for the Flow card (if available in your version). From there you can:

  • Inspect available workflows.

  • Trigger them with custom input data.

Even without a dedicated card, you can:

  • Hit the REST endpoint from the Dev UI REST Client panel (if you expose an OpenAPI description).

  • Watch logs and tracing output as the workflow runs.

6. What you learned

  • Quarkus Flow discovers Flow subclasses at build time and makes them injectable.

  • You can expose workflows through normal Quarkus REST endpoints using Uni.

  • Dev UI gives you a convenient front-end for iterating on workflows while in dev mode.