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:

src/main/flow/echo-name.yaml
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 document section (DSL version, namespace, name, version)

  • defines a single set task that writes a message field into the workflow data based on the input .name

You can customize the directory location by setting the quarkus.flow.definitions.dir property in your application.properties.

The value can be:

  • an absolute path, or

  • a relative path resolved from the Maven module root (the directory that contains your pom.xml).

By default it is src/main/flow. All .yaml and .yml files in the resolved directory are discovered at build time.

The file name (for example, echo-name.yaml) is not used as the identifier. Quarkus Flow uses document.namespace and document.name from the YAML as the canonical workflow identifier.

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 @Identifier value must exactly match document.namespace:document.name in your YAML specification.

If you change either document.namespace or document.name, you must update every injection point that references this workflow.

A typical execution flow in this example looks like:

  1. definition.instance(Map.of("name", finalName)) creates a new workflow instance with the initial data (name).

  2. .start() begins the workflow execution and returns a CompletionStage<WorkflowModel>.

  3. .thenApply(result → result.asText().orElseThrow()) transforms the WorkflowModel into a JSON String representation of the workflow data.

  4. 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 the CompletionStage is 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 Response or 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 WorkflowDefinition bean

  • wire it into your EchoResource via CDI

Any change to the YAML file is picked up by Quarkus dev mode with a live reload, just like regular Java code.

4. Test the workflow

Call the REST endpoint:

curl "http://localhost:8080/echo?name=John"

5. Expected outcome

After completing these steps, you should have:

  • a workflow specification file loaded automatically by Quarkus Flow

  • a WorkflowDefinition CDI 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"}