Lab 2 – Call HTTP & OpenAPI services

In this lab you:

  • Add an HTTP call to a secured endpoint.

  • Call an OpenAPI operation by operationId.

  • See how WorkflowException is mapped to an RFC 7807 / WorkflowError response.

  • Use Dev UI and logs to troubleshoot.

We will reuse the existing example classes:

  • org.acme.example.CustomerProfileFlow

  • org.acme.example.CustomerProfileResource

  • org.acme.example.PetstoreFlow

1. Add HTTP client configuration

First, configure basic HTTP timeouts and logging:

quarkus.flow.http.client.connect-timeout=5000
quarkus.flow.http.client.read-timeout=10000
quarkus.flow.http.client.logging.scope=request-response
quarkus.flow.http.client.logging.body-limit=2048
quarkus.log.category."org.jboss.resteasy.reactive.client.logging".level=DEBUG

For details see Configure the HTTP client.

2. Add the HTTP workflow

Create a workflow that calls a secured endpoint and uses Basic auth via secrets:

package org.acme.example;

import static io.serverlessworkflow.fluent.func.FuncWorkflowBuilder.workflow;
import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.get;
import static io.serverlessworkflow.fluent.spec.dsl.DSL.basic;
import static io.serverlessworkflow.fluent.spec.dsl.DSL.secret;

import java.net.URI;

import jakarta.enterprise.context.ApplicationScoped;

import org.eclipse.microprofile.config.inject.ConfigProperty;

import io.quarkiverse.flow.Flow;
import io.serverlessworkflow.api.types.Workflow;

@ApplicationScoped
public class CustomerProfileFlow extends Flow {

    @ConfigProperty(name = "demo.server", defaultValue = "http://localhost:8080")
    String securedServer;

    @Override
    public Workflow descriptor() {
        URI endpoint = URI.create(securedServer + "/secure/profile");

        return workflow("secure-customer-profile")
                // Load secrets into workflow data (see secrets documentation)
                .use(secret("demo"))
                .tasks(
                        // GET with HTTP Basic credentials taken from the secret
                        get(endpoint, basic("${ $secret.demo.username }", "${ $secret.demo.password }")))
                .build();
    }
}

This workflow uses:

  • get(…​) from the Func DSL to perform an HTTP GET.

  • basic(…​) auth wired to secret values (demo.username / demo.password).

  • Standard data transformations to shape the response.

Configure the demo credentials in application.properties as shown in Call HTTP and OpenAPI services.

You can see a fully functional project of this example at https://github.com/quarkiverse/quarkus-flow/tree/main/examples/http-basic-auth.

3. Expose the workflow as a REST endpoint

Create a resource that invokes the workflow:

package org.acme.example;

import java.util.Map;
import java.util.concurrent.CompletionStage;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/api/profile")
@Produces(MediaType.APPLICATION_JSON)
public class CustomerProfileResource {

    @Inject
    CustomerProfileFlow customerProfileFlow;

    @GET
    public CompletionStage<Map<String, Object>> getProfileViaWorkflow() {
        return customerProfileFlow.instance(Map.of()).start().thenApply(r -> r.asMap().orElseThrow());
    }
}

Notice:

  • The method returns CompletionStage<Response> and calls instance(…​).start().

  • If the HTTP call fails (for example, 401 or 404), a WorkflowException is thrown.

  • Quarkus Flow’s ExceptionMapper<WorkflowException> returns a Problem Details JSON body based on the underlying WorkflowError.

4. Try a failing call

With dev mode running:

  • Call the endpoint with wrong credentials or misconfigured auth.

  • Observe the HTTP response:

HTTP/1.1 401 Unauthorized
Content-Type: application/json

{
  "type": "https://serverlessworkflow.io/spec/1.0.0/errors/communication",
  "status": 401,
  "title": "HTTP 401 Unauthorized",
  "instance": "do/0/..."
}
  • Open Dev UI and look at:

    • REST call in the REST Client (if OpenAPI is enabled).

    • Logs from the REST client logger and from Flow tracing.

5. Call an OpenAPI operation

Add a workflow that calls the Petstore demo by operationId:

package org.acme.example;

import static io.serverlessworkflow.fluent.func.FuncWorkflowBuilder.workflow;
import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.openapi;

import java.net.URI;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkiverse.flow.Flow;
import io.serverlessworkflow.api.types.Workflow;

@ApplicationScoped
public class PetstoreFlow extends Flow {

    @Override
    public Workflow descriptor() {
        final URI petstoreUri = URI.create("openapi/petstore.json");

        return workflow("petstore")
                .tasks(
                        // 1) Find pets by status
                        openapi()
                                .document(petstoreUri).operation("findPetsByStatus").parameter("status", "sold")
                                // Pick the first pet id and expose it as `selectedPetId`
                                .outputAs("${ { selectedPetId: .[0].id } }"),
                        // 2) Fetch that pet by id
                        openapi()
                                .document(petstoreUri).operation("getPetById").parameter("petId", "${ .selectedPetId }"))
                .build();
    }
}

and put the petstore.json OpenAPI document under src/main/resources/openapi/petstore.json (or similar) as shown in Call HTTP and OpenAPI services.

You can see a fully functional project of this example at https://github.com/quarkiverse/quarkus-flow/tree/main/examples/petstore-openapi.

Run the workflow and:

  • Inspect how the operation is resolved by operationId.

  • Use Dev UI and logs to verify the actual URL, method and payload being used.

6. What you learned

  • How to perform HTTP and OpenAPI calls from workflows.

  • How to configure HTTP behaviour (timeouts, logging, auth).

  • How HTTP failures become WorkflowException / WorkflowError Problem Details responses.

  • How Dev UI + logs make troubleshooting much easier.