Workflow definitions from YAML files
This guide shows you how to load CNCF Workflow definitions (YAML DSL) into your Quarkus application and expose them as WorkflowDefinition/Flow CDI beans you can inject and execute.
Quarkus Flow recommends the Java DSL as the primary way to define workflows in Quarkus applications, because it gives you type safety, refactor-friendly code and excellent IDE support. Loading workflows from YAML is available as an alternative for teams that already maintain CNCF Workflow specification files or need to share definitions across runtimes. For the Java DSL reference, see Java DSL cheatsheet.
If you are new to the CNCF Workflow concepts, see CNCF Workflow mapping and concepts first.
Prerequisites
-
A Quarkus application with Quarkus Flow set up.
-
A directory that will hold your workflow definitions:
-
by default:
src/main/flow -
or a custom directory configured via
quarkus.flow.definitions.dir
-
-
Basic familiarity with YAML and the CNCF Workflow DSL.
1. Create the workflow specification file
By default, Quarkus Flow scans the src/main/flow directory (relative to your Maven module root)
for workflow specification files.
Create the directory and add a simple workflow:
document:
dsl: '1.0.0'
namespace: company
name: echo-name
version: '0.1.0'
do:
- setEcho:
set:
message: '${ "echo: " + .name }'
This definition:
-
declares the workflow metadata in the
documentsection (DSL version, namespace, name, version) -
defines a single
settask that writes amessagefield into the workflow data based on the input.name
|
You can customize the directory location by setting the
The value can be:
By default it is |
2. Inject the Flow bean
For each discovered specification file, Quarkus Flow generates a WorkflowDefinition and Flow CDI beans.
|
Quarkus Flow uses the The extension creates two types of identifiers (
All identifiers can be used for |
Create a JAX-RS resource that injects and runs the workflow:
package org.acme;
import java.util.Map;
import java.util.Objects;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import io.quarkiverse.flow.Flow;
import io.smallrye.common.annotation.Identifier;
import io.smallrye.mutiny.Uni;
@Path("/echo")
public class EchoResource {
@Inject
@Identifier("company.EchoName") (1)
Flow flow;
@GET
public Uni<String> echo(@QueryParam("name") String name) {
final String finalName = Objects.requireNonNullElse(name, "(Duke)");
return flow.startInstance(Map.of("name", finalName))
.onItem()
.transform(wf -> wf.asText().orElseThrow());
}
}
| 1 | Use the @Identifier annotation with the Fully Qualified Class Name from your YAML file. |
|
The If you change either |
|
You can define a global namespace prefix for Fully Qualified Class Name identifiers using the |
A typical execution flow in this example looks like:
-
flow.startInstance(Map.of("name", finalName))creates a newio.smallrye.mutiny.Uni<WorkflowModel>instance with the initial data (name). -
.onItem().transform(…)observes and transform the item emitted byUniand creates a newUni<String>with the JSON representation of the workflow data. -
The JAX-RS method returns a
Uni<String>, so the endpoint remains reactive/non-blocking.
2.1 Blocking vs non-blocking REST endpoints
The EchoResource above uses the recommended non-blocking style for Quarkus REST endpoints:
-
the method returns
Uni<String> -
the workflow is started with
startInstance(…)and theUniis propagated back to the caller
This style integrates well with Quarkus’ reactive model and, when a workflow is invoked
from a JAX-RS resource, lets any WorkflowException propagate directly to the HTTP layer.
Quarkus Flow registers a standard JAX-RS ExceptionMapper<WorkflowException>, so any
resource that throws WorkflowException (including those that call workflows) will
automatically be translated into an RFC 7807 / WorkflowError HTTP response.
If you prefer a blocking style in very simple scenarios (for example in a CLI or quick prototype), you can:
-
call
.instance().start().join()using aCompletionStageor.await().indefinetely()when using Mutiny to wait for the result, and return aResponseor a plain DTO from your JAX-RS method.
In that case, be aware that blocking on a CompletionStage/Uni may wrap underlying exceptions
(for example in ExecutionException, CompletionException), and you might need to unwrap and rethrow WorkflowException
if you want the standard HTTP error mapping.
For a deeper discussion and a full HTTP/OpenAPI example, see
Non-blocking vs blocking style.
3. Run the application
Start your Quarkus application in development mode:
./mvnw quarkus:dev
Quarkus Flow will:
-
discover the YAML file in
src/main/flow. -
compile it into a
Flowbean. -
wire it into your
EchoResourcevia CDI.
Any change to the YAML file is picked up by Quarkus dev mode with a live reload, just like regular Java code.
5. Expected outcome
After completing these steps, you should have:
-
a workflow specification file loaded automatically by Quarkus Flow
-
a
FlowCDI bean available for injection using@Identifier("company.EchoName") -
a working REST endpoint that executes the workflow and returns the result
The expected JSON response from the curl command is:
{"message":"echo: John"}