π Language: English | Π ΡΡΡΠΊΠΈΠΉ
The HTTP client uses a middleware pipeline for processing requests and responses.
Middleware are executed in descending priority order. Higher priority = executed earlier.
Request Flow (high β low priority):
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β HTTP Client β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β [100] AuthMiddleware β
β Adds Authorization header β
β Why first: credentials must be added before retry β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β [90] RetryMiddleware β
β Retries request on errors (429, 5xx) β
β Why here: retry should encompass logging and circuit β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β [80] LoggingMiddleware β
β Logs each request attempt β
β Why here: log each retry, but after auth β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β [60] CircuitBreakerMiddleware β
β Blocks requests to "failed" services β
β Why here: circuit considers each retry attempt β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β [10] MultiHostMiddleware β
β Selects host by strategy (failover, round-robin, parallel) β
β Why last: executes immediately before transport β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β GuzzleTransport β
β Sends HTTP request β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Priority | Middleware | Purpose | Enabled by Default |
|---|---|---|---|
| 100 | AuthMiddleware |
Adds authentication headers | Yes |
| 90 | RetryMiddleware |
Retries with exponential backoff | Yes (configurable) |
| 80 | LoggingMiddleware |
Logs requests/responses | Yes (configurable) |
| 60 | CircuitBreakerMiddleware |
Circuit Breaker pattern | No (by default) |
| 10 | MultiHostMiddleware |
Multi-host strategy handling | Automatic |
Authentication must be added before any retry logic. If auth were added after retry:
Retry should encompass all downstream middleware:
Located inside the retry cycle, so:
Checks circuit state for each attempt:
Host selection happens immediately before sending:
Middleware supports both synchronous and asynchronous requests through Promise-based architecture (like Guzzle).
<?php
declare(strict_types=1);
namespace App\Middleware;
use Deeep\ServiceClient\Context\RequestContext;
use Deeep\ServiceClient\Contract\MiddlewareInterface;
use Deeep\ServiceClient\Contract\RequestInterface;
use Deeep\ServiceClient\Contract\ResponseInterface;
use GuzzleHttp\Promise\PromiseInterface;
use Throwable;
final readonly class TimingMiddleware implements MiddlewareInterface
{
private const int PRIORITY = 85;
public function __construct(
private MetricsCollector $metrics,
) {}
public function process(
RequestInterface $request,
RequestContext $context,
callable $next,
): ResponseInterface|PromiseInterface {
$startTime = microtime(true);
try {
$result = $next($request, $context);
} catch (Throwable $e) {
$this->recordMetrics($request, $startTime, false);
throw $e;
}
if ($result instanceof PromiseInterface) {
return $result->then(
function (ResponseInterface $response) use ($request, $startTime): ResponseInterface {
$this->recordMetrics($request, $startTime, true);
return $response;
},
function (Throwable $e) use ($request, $startTime): void {
$this->recordMetrics($request, $startTime, false);
throw $e;
},
);
}
$this->recordMetrics($request, $startTime, true);
return $result;
}
public function getPriority(): int
{
return self::PRIORITY;
}
private function recordMetrics(RequestInterface $request, float $startTime, bool $success): void
{
$this->metrics->recordTiming(
service: $request->getService(),
duration: microtime(true) - $startTime,
success: $success,
);
}
}
$next() returns ResponseInterface for sync or PromiseInterface for async->then(onSuccess, onError)onError callbackImplement MiddlewareInterface β Symfony will automatically register the middleware:
// src/Middleware/TimingMiddleware.php
// Just implement MiddlewareInterface - autoconfiguration via instanceof
<?php
// config/services.php
use App\Middleware\TimingMiddleware;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $container): void {
$services = $container->services();
$services->set(TimingMiddleware::class)
->tag('deeep_service_client.middleware');
};
| Range | Purpose | Examples |
|---|---|---|
| 95-100 | Request modification | Auth, Request ID, Tenant headers |
| 85-94 | Metrics/timing (outside retry) | Prometheus metrics, APM |
| 75-89 | Inside retry cycle | Logging, tracing |
| 50-74 | Protection patterns | Circuit breaker, rate limiting |
| 1-49 | Transport level | Multi-host, caching |
'services' => [
'fast_api' => [
'host' => '...',
'retry' => [
'enabled' => false,
],
],
],
Disabled by default. To enable:
'services' => [
'unreliable_api' => [
'host' => '...',
'circuit_breaker' => [
'enabled' => true,
'failure_threshold' => 5,
'open_timeout' => 30,
],
],
],
'services' => [
'internal_api' => [
'host' => '...',
'logging' => [
'enabled' => false,
],
],
],
How can I help you explore Laravel packages today?