cleverage/rest-process-bundle
CleverAge/RestProcessBundle adds REST integration to the CleverAge ProcessBundle for Symfony, letting you connect process workflows to HTTP APIs. Compatible with stable Symfony and LTS releases; pair it with the core ProcessBundle docs.
First, calibrate expectations: this is a Symfony bundle, not a Laravel package. Laravel does not boot Symfony bundles, so RestProcessBundle, its DI extension, and its compiler pass will never execute inside a Laravel application. What you can reuse is the runtime layer — Client, ClientRegistry, RequestTask, MissingClientException — provided those classes are framework-agnostic enough to be instantiated by hand. Treat the bundle wiring as something you re-implement in a service provider, not something you consume.
Minimal steps
composer require cleverage/rest-process-bundle (pulls symfony/http-client; v2.0+ dropped nategood/httpful).docs/index.md first, then in this order:
Client — base URI, headers, auth, getUri().ClientRegistry — keyed by client code, populated by the bundle's compiler pass.Task/RequestTask — the object you'll actually call.MissingClientException — what you get for an unknown code.First use case: a single outbound call to a third-party API, with a per-call expected-status rule and logging of what actually went over the wire.
// app/Providers/RestProcessServiceProvider.php (illustrative — verify signatures in docs/index.md)
public function register(): void
{
$this->app->singleton(ClientRegistry::class, function () {
$registry = new ClientRegistry();
foreach (config('rest_process.clients', []) as $code => $definition) {
$registry->addClient($this->makeClient($definition), $code);
}
return $registry;
});
$this->app->bind(RequestTask::class, fn ($app) => new RequestTask(
$app->make(ClientRegistry::class)
));
}
If you have exactly one simple outbound call, stop here and use Laravel's Http:: facade. The registry/task machinery only pays for itself once you have several declared clients plus per-call validation and logging rules.
The pragmatic integration modes, in order of preference
| Mode | When | Cost |
|---|---|---|
| Dedicated Symfony worker consuming a queue, Laravel talks to it | You already run cleverage/process-bundle pipelines |
An extra deployable |
Manual re-binding of Client / ClientRegistry / RequestTask in a Laravel provider |
A handful of clients, you want the validation/logging semantics | You own the wiring |
Skip it — Http:: + a small wrapper |
Simple calls, no pipeline | Least code |
1. Config-driven client registration (the compiler-pass replacement)
In Symfony the bundle registers Client services by code via a compiler pass. In Laravel you rebuild that in a provider from config/rest_process.php, keyed by the same client codes. Keep it a flat map so duplicate-code detection is a one-line isset() check — and make the exception message name both entries, mirroring the v3.2 improvement (#33) that names both colliding service IDs. Misconfigured clients should fail at boot, not at first call.
2. Base client + per-call overrides
Define base URI, default headers, and auth once per client code; pass per-call specifics through request_options rather than mutating the Client. This is the pattern the package is shaped around, and it keeps clients reusable and mockable.
3. RequestTask as the unit of work
The task takes an array input and performs the HTTP call. Two semantics to internalise:
data, not query_parameters — the latter is deprecated since v2.0.send: application/json, the body must be sent as JSON (a 2.1 fix). Verify the content type is actually applied; it's the kind of thing that silently regresses.valid_response_code is not a success list. A 3xx/4xx/5xx code listed there still fails the task; since v3.2 it correctly returns the response body so you can inspect it (#35). Code written against the old empty-body behaviour needs review.4. Treat expected failures as data
The strongest use case for this package: third-party APIs where a 404 or 422 is a legitimate, expected outcome you want to branch on. List the code in valid_response_code, catch the task failure, read the body, and route accordingly. Combine with v2.2's behaviour: a ClientExceptionInterface is permitted if a 4xx code is listed, and RedirectionExceptionInterface if a 3xx is listed.
5. Logging pattern worth wiring explicitly
log_response on tasks you care about. Since v3.2 it logs the requested URL plus status, headers, and content — not the configured URL and a response object dump (#37).>=3.2 if diagnosability is why you're here.request_options in the failure context (added in 2.1).6. Wrapping in queue jobs
The natural Laravel shape: one job per HTTP-bound process step, wrapping a RequestTask call. Keep retries and backoff in the job ($tries, $backoff), timeouts on the underlying HTTP client — not on the task. Make steps idempotent via a request header, since queue retries will re-fire calls. Note that tasks are shared: false, so don't carry task state across a process; construct per execution.
7. Testing
Mock Symfony\Contracts\HttpClient\HttpClientInterface and inject it into Client — v3.2 added exactly this coverage. In Laravel, bind a mock HttpClientInterface in the container for the test. Also test the UnexpectedValueException path: v3.2 made non-array input to RequestTask throw explicitly (#38), so guard and assert on it.
8. Extension points to keep in mind
ClientRegistry::addClient() accepts an optional $serviceId (used for the duplicate-code error) — pass your config key or provider identifier through it even outside Symfony.Client for auth schemes (token refresh, signing) rather than adding middleware elsewhere.RequestTask for cross-cutting concerns (metrics, correlation IDs) instead of editing the call site.HttpClientInterface adapter — a thin bridge over Laravel's Http factory is the cheapest way to get Laravel's logging and mocking story under this package's API.FrameworkBundle, no compiler pass, no services.yaml. Services are explicitly defined and prefixed cleverage_rest_process — they are not autowired or autoconfigured, so app()->make(ClientRegistry::class) won't resolve anything until you define it. Don't assume container magic.valid_response_code ≠ success. The task still fails for listed 3xx/4xx/5xx codes. If your code asserts "listed code means we passed", it's wrong — and it was differently wrong before v3.2, when the body came back empty.Client::geUri() → Client::getUri(). Mechanical, greppable, and the only deprecation in 3.2. Do the rename before the next major rather than scheduling a project around it.TypeError (#38). Pre-v3.2, passing an int where a string was expected blew up in an unhelpful place.query_parameters is deprecated. Use data. If you copy a 1.x-era snippet, you'll be on the deprecated path immediately.? / & in request URIs was fixed back in 1.0.2. Only relevant if you pin an ancient version — but a stale lockfile is a common reason for "my query string has a stray ampersand".RequestTask in 1.0.1 and substantially improved in 1.0.1/3.2. If your installed version is old, expect poor failure messages and don't conclude the package is opaque.docs/index.md before shipping: the exact top-level config key, the shape of a client definition (base URI, headers, auth), and whether request_options merge shallowly or deeply over client defaults. Merging semantics bite hardest with nested headers.HttpClientInterface implementation. If you also retry in the Laravel job, you've doubled your retry budget — pick one layer.How can I help you explore Laravel packages today?