Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Service Client Bundle Laravel Package

deeep/service-client-bundle

View on GitHub
Deep Wiki
Context7

Middleware

🌐 Language: English | Русский

The HTTP client uses a middleware pipeline for processing requests and responses.

Execution Order

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                               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Built-in Middleware

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

Why This Order?

AuthMiddleware (100) β€” First

Authentication must be added before any retry logic. If auth were added after retry:

  • First request would go without credentials
  • Retry would add auth repeatedly

RetryMiddleware (90) β€” After Auth

Retry should encompass all downstream middleware:

  • Log each attempt
  • Consider circuit breaker state
  • Try different hosts on failover

LoggingMiddleware (80) β€” Logs Each Attempt

Located inside the retry cycle, so:

  • Each attempt is logged, not just the final result
  • Credentials are already added (not logged separately)

CircuitBreakerMiddleware (60) β€” Cascade Failure Protection

Checks circuit state for each attempt:

  • If circuit is open β€” request is not sent
  • Failure/success are counted in statistics

MultiHostMiddleware (10) β€” Last

Host selection happens immediately before sending:

  • Failover tries next host on error
  • Round-robin rotates between hosts
  • Parallel sends to all hosts

Creating Custom Middleware

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,
        );
    }
}

Important: Promise-based Architecture

  • $next() returns ResponseInterface for sync or PromiseInterface for async
  • For async, handle result via ->then(onSuccess, onError)
  • The same middleware works for both synchronous and asynchronous requests
  • Synchronous exceptions are caught in try-catch, asynchronous β€” in onError callback

Registering Middleware

Automatic (Recommended)

Implement MiddlewareInterface β€” Symfony will automatically register the middleware:

// src/Middleware/TimingMiddleware.php
// Just implement MiddlewareInterface - autoconfiguration via instanceof

Manual Registration

<?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');
};

Priority Recommendations

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

Disabling Built-in Middleware

Retry

'services' => [
    'fast_api' => [
        'host' => '...',
        'retry' => [
            'enabled' => false,
        ],
    ],
],

Circuit Breaker

Disabled by default. To enable:

'services' => [
    'unreliable_api' => [
        'host' => '...',
        'circuit_breaker' => [
            'enabled' => true,
            'failure_threshold' => 5,
            'open_timeout' => 30,
        ],
    ],
],

Logging

'services' => [
    'internal_api' => [
        'host' => '...',
        'logging' => [
            'enabled' => false,
        ],
    ],
],
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle