gRPC Channel Routing

Preview — This feature is functional and tested, but backward compatibility is not guaranteed across releases.

Quarkus Flow supports gRPC through the quarkus-flow-grpc module. It connects the Open Workflow gRPC executor to Quarkus gRPC channels, so you can reuse the same client routing and configuration you already use in Quarkus applications.

Add the dependency

<dependency>
    <groupId>io.quarkiverse.flow</groupId>
    <artifactId>quarkus-flow-grpc</artifactId>
</dependency>

Channel resolution

The module resolves the Quarkus gRPC named channel to use for each workflow task. When multiple routing rules match, the most specific one wins:

  1. Task-level full (most specific) — quarkus.flow.grpc.client."<namespace>\:<name>\:<version>.task.<taskName>".name

  2. Task-level mediumquarkus.flow.grpc.client."<namespace>\:<name>.task.<taskName>".name

  3. Task-level shortquarkus.flow.grpc.client."<name>.task.<taskName>".name

  4. Workflow-level fullquarkus.flow.grpc.client."<namespace>\:<name>\:<version>".name

  5. Workflow-level mediumquarkus.flow.grpc.client."<namespace>\:<name>".name

  6. Workflow-level shortquarkus.flow.grpc.client.<name>.name

  7. Default channel — if a Quarkus gRPC client named flowGrpc exists, it is used for all workflows

  8. SDK fallback — no routing configured; the SDK falls back to the host and port declared in the workflow itself

Progressive specificity: The resolver tries keys from most to least specific: first task-level (full, medium, short), then workflow-level (full, medium, short). Within each group, it goes from longest (namespace:name:version) to shortest (name only).

Colons in the composite key must be escaped as \: in application.properties files. For example, for a workflow with namespace org.acme and name grpcGreeting, use "org.acme\:grpcGreeting".

Configure a default channel

If all your workflows contact the same gRPC server, configure a single default client:

quarkus.grpc.clients.flowGrpc.host=localhost
quarkus.grpc.clients.flowGrpc.port=9000
quarkus.grpc.clients.flowGrpc.plain-text=true

All workflows will automatically use the flowGrpc channel.

Configure per-workflow channels

To route a specific workflow to a different gRPC server:

quarkus.grpc.clients.myService.host=my-grpc-server.example.com
quarkus.grpc.clients.myService.port=443
quarkus.grpc.clients.myService.plain-text=false

# Short key — route by workflow name only (simplest)
quarkus.flow.grpc.client.grpcGreeting.name=myService

# Medium key — route by namespace and name (all versions)
quarkus.flow.grpc.client."org.acme\:grpcGreeting".name=myService

# Full key — route a specific version
quarkus.flow.grpc.client."org.acme\:grpcGreeting\:0.0.1".name=myService

A more specific key always takes precedence: full (org.acme:grpcGreeting:0.0.1) wins over medium (org.acme:grpcGreeting), which wins over short (grpcGreeting).

Configure per-task channels

If a single workflow orchestrates multiple gRPC servers, use task-level overrides. Each gRPC task in the workflow must have a unique name.

# Workflow-level default
quarkus.flow.grpc.client."org.acme\:orderWorkflow\:0.0.1".name=orderService

# Task-level override (uses .task. separator)
quarkus.flow.grpc.client."org.acme\:orderWorkflow\:0.0.1.task.checkInventory".name=inventoryService

# Short task key — by workflow name and task only
quarkus.flow.grpc.client."orderWorkflow.task.checkInventory".name=inventoryService

Task names within a workflow must be distinct if you need different gRPC clients for different tasks.

Proto file path

The quarkus-flow-grpc module provides a classpath-aware resource loader. You can reference proto files directly from the classpath without extracting them to temporary files:

import static io.serverlessworkflow.fluent.spec.dsl.DSL.grpc;

@ApplicationScoped
public class GrpcGreetingFlow extends Flow {
    @Override
    public Workflow descriptor() {
        return WorkflowBuilder.workflow("grpcGreeting")
                .tasks(tasks -> tasks.grpc("greet",
                        grpc()
                                .proto("proto/greeter.proto")
                                .service("Greeter", "localhost")
                                .method("SayHello")
                                .argument("name", "${ .name }")))
                .build();
    }
}

The proto file at src/main/resources/proto/greeter.proto is resolved from the classpath automatically.

Native image: proto files are read from the classpath at runtime, so Quarkus Flow does not bundle them into the native image automatically. If you build a native executable, don’t forget to register your proto files as native resources, otherwise the workflow fails at startup with a missing-resource error:

quarkus.native.resources.includes=proto/greeter.proto

You can also use a pattern to include every proto under a directory:

quarkus.native.resources.includes=proto/*

Example project

See the full runnable example at examples/grpc-client-routing.