Weave 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

Http Pool Bundle Laravel Package

aubes/http-pool-bundle

View on GitHub
Deep Wiki
Context7
## Getting Started

### Minimal Setup
1. **Install the package**:
   ```bash
   composer require aubes/http-pool-bundle
  1. Register the bundle in config/bundles.php (auto-registered in Symfony 6.4+):
    Aubes\HttpPoolBundle\HttpPoolBundle::class => ['all' => true],
    
  2. Configure defaults (optional) in config/packages/http_pool.yaml:
    http_pool:
        default_concurrency: 10
        error_strategy: collect
    
  3. Inject the factory into your service:
    use Aubes\HttpPoolBundle\Pool\PoolFactoryInterface;
    
    public function __construct(
        private readonly PoolFactoryInterface $httpPool,
    ) {}
    

First Use Case: Parallel API Calls

public function fetchUserData(int $userId): array
{
    $pool = $this->httpPool->create(concurrency: 5);
    $pool->add('user', 'GET', "https://api.example.com/users/{$userId}");
    $pool->add('orders', 'GET', "https://api.example.com/orders?user={$userId}");

    $results = $pool->flush();
    return [
        'user' => $results->get('user')->toArray(),
        'orders' => $results->get('orders')->toArray(),
    ];
}

Key files to reference:

  • src/Pool/PoolFactoryInterface.php (core interface)
  • src/Pool/PoolInterface.php (builder methods)
  • src/Pool/PoolResults.php (results handling)
  • config/packages/http_pool.yaml (configuration)

Implementation Patterns

1. Request Scoping with Disposable Pools

// Per-request pool (auto-cleared after flush)
$pool = $this->httpPool->create(concurrency: 10);
$pool->add('data', 'GET', 'https://api.example.com/data');
$results = $pool->flush(); // Pool is now empty

Best Practice: Always use create() for new operations. Avoid reusing pools across requests to prevent memory leaks.


2. Reactive Fan-Out with then()

$pool->add('user', 'GET', 'https://api.example.com/users/1')
    ->then(function (ResponseInterface $response, PoolInterface $pool) {
        $user = $response->toArray();
        $pool->add('posts', 'GET', "https://api.example.com/posts?user={$user['id']}");
        $pool->add('profile', 'GET', $user['profile_url']);
    });

Pattern: Use then() for:

  • Dynamic request generation (e.g., fetching related resources).
  • Chaining dependent API calls (e.g., user → posts → comments).
  • Avoid: Deep nesting (>3 levels). Prefer flattening with addOnce() for shared data.

3. Error Handling Strategies

Strategy Use Case Example
collect Log errors silently (default). $pool->create(errorStrategy: ErrorStrategy::Collect)
stop_on_first Fail fast on critical errors. $pool->create(errorStrategy: ErrorStrategy::StopOnFirst)
throw_all Aggregate all errors for batch processing. $pool->create(errorStrategy: ErrorStrategy::ThrowAll)

Pattern:

$pool->add('fallback', 'GET', 'https://api.example.com/fallback')
    ->catch(function (\Throwable $e) {
        // Custom fallback logic
        return new Response(json_encode(['status' => 'fallback']));
    });

4. Deduplication with addOnce()

// Shared resource (e.g., brand data)
$pool->addOnce('brand_123', 'GET', 'https://api.example.com/brands/123')
    ->then(function (ResponseInterface $response) {
        $brand = $response->toArray();
        // Shared across all consumers
    });

// Multiple consumers trigger the same request
$pool->add('product_1', 'GET', 'https://api.example.com/products/1')
    ->then(fn($res) => $pool->addOnce('brand_' . $res->toArray()['brandId'], ...));

$pool->add('product_2', 'GET', 'https://api.example.com/products/2')
    ->then(fn($res) => $pool->addOnce('brand_' . $res->toArray()['brandId'], ...));

Pattern:

  • Use addOnce() for shared resources (e.g., brands, categories, users).
  • Avoid in high-cardinality scenarios (e.g., per-item requests).

5. Rate Limiting and Retry

# config/packages/http_pool.yaml
http_pool:
    rate_limits:
        'api.example.com': 10  # 10 req/s
    retry:
        503: { max: 3, delay: 1000 }  # Retry 503 up to 3x with 1s delay
        429: respect_retry_after     # Honor Retry-After header

Pattern:

  • Rate Limiting: Apply per-host limits to avoid API bans.
  • Retry: Configure per-status-code (e.g., 503, 429).
  • Debug: Use the Profiler panel to monitor rate limits and retries.

6. Fire-and-Forget

$pool->fire('POST', 'https://analytics.example.com/event', [
    'json' => ['event' => 'user_viewed', 'user_id' => 123],
]);
// No need to handle the response

Use Case:

  • Analytics tracking.
  • Background notifications.
  • Avoid for critical operations (no error feedback).

7. Integration with Symfony Components

Serializer Denormalization

use Symfony\Component\Serializer\Normalizer\ContextAwareInterface;

public function normalize($object, string $format = null, array $context = []): array
{
    $pool = $context['pool'] ?? $this->httpPool->create();
    $data = $object->toArray();

    // Fetch related resources
    $pool->addOnce('author', 'GET', "https://api.example.com/authors/{$data['author_id']}");
    $pool->addOnce('tags', 'GET', "https://api.example.com/tags/{$data['tag_ids']}");

    $results = $pool->flush();
    return [
        ...$data,
        'author' => $results->get('author')->toArray(),
        'tags' => $results->get('tags')->toArray(),
    ];
}

Pattern:

  • Pass the pool via denormalization_context:
    # config/services.yaml
    App\Serializer\PoolAwareNormalizer:
        arguments:
            $httpPool: '@Aubes\HttpPoolBundle\Pool\PoolFactoryInterface'
    

8. Testing Patterns

public function testConcurrentRequests(): void
{
    $pool = $this->httpPool->create(concurrency: 2);
    $pool->add('user', 'GET', 'https://api.example.com/users/1');
    $pool->add('posts', 'GET', 'https://api.example.com/posts/1');

    // Mock responses
    $this->httpClient->mock([
        'GET https://api.example.com/users/1' => new Response(200, [], '{"id":1}'),
        'GET https://api.example.com/posts/1' => new Response(200, [], '[{"id":1}]'),
    ]);

    $results = $pool->flush();
    $this->assertEquals(1, $results->get('user')->toArray()['id']);
}

Tools:

  • Use HttpClientInterface::mock() for isolated tests.
  • Verify PoolResults structure:
    $this->assertCount(2, $results->getKeys());
    $this->assertEquals('collect', $results->getErrorStrategy());
    

Gotchas and Tips

1. Concurrency Limits

  • Pitfall: Setting concurrency: 0 disables parallelism entirely (all requests run sequentially).
  • Fix: Always specify a positive integer (e.g., concurrency: 5).
  • Tip: Monitor concurrency in the Profiler panel to avoid overwhelming APIs.

2. Callback Execution Order

  • Gotcha: then() callbacks run asynchronously after the HTTP response arrives.
    $pool->add('user', 'GET', 'https://api.example.com/users/1')
        ->then(fn($res) => $this->log("User fetched")); // Runs later!
    
  • Tip: Use flush() to wait for all callbacks before proceeding.

3. Deduplication Quirks

  • Gotcha: addOnce() deduplicates by key only. If two requests
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.
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus