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 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: flow
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 |
|
The file name (for example, |
2. Inject the WorkflowDefinition bean
For each discovered specification file, Quarkus Flow generates a WorkflowDefinition CDI bean.
The bean qualifier follows the pattern {namespace}:{name} from your YAML file.
Create a JAX-RS resource that injects and runs the workflow:
package org.acme;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletionStage;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import io.serverlessworkflow.impl.WorkflowDefinition;
import io.smallrye.common.annotation.Identifier;
@Path("/echo")
public class EchoResource {
@Inject
@Identifier("flow:echo-name") (1)
WorkflowDefinition definition;
@GET
public CompletionStage<String> echo(@QueryParam("name") String name) {
final String finalName = Objects.requireNonNullElse(name, "(Duke)");
return definition.instance(Map.of("name", finalName))
.start()
.thenApply(result -> result.asText().orElseThrow());
}
}
| 1 | Use the @Identifier annotation with the format {namespace}:{name} from your YAML file,
for example flow:echo-name. |
|
The If you change either |
A typical execution flow in this example looks like:
-
definition.instance(Map.of("name", finalName))creates a new workflow instance with the initial data (name). -
.start()begins the workflow execution and returns aCompletionStage<WorkflowModel>. -
.thenApply(result → result.asText().orElseThrow())transforms theWorkflowModelinto a JSONStringrepresentation of the workflow data. -
The JAX-RS method returns a
CompletionStage<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
CompletionStage<String> -
the workflow is started with
instance(…).start()and theCompletionStageis 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
.start().join()to wait for the result, and -
return a
Responseor a plain DTO from your JAX-RS method.
In that case, be aware that blocking on a CompletionStage may wrap underlying exceptions
(for example in ExecutionException), 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
CompletionStage 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 the configured directory (by default
src/main/flow) -
compile it into a
WorkflowDefinitionbean -
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
WorkflowDefinitionCDI bean available for injection using@Identifier("flow:echo-name") -
a working REST endpoint that executes the workflow and returns the result
The expected JSON response from the curl command is:
{"message":"echo: John"}