symfony/http-client
Symfony HttpClient provides a robust API to fetch HTTP resources synchronously or asynchronously. It supports modern features like concurrent requests and streaming, and integrates cleanly with the Symfony ecosystem for building reliable HTTP clients.
## Getting Started
### Minimal Setup in Laravel
Install the package via Composer:
```bash
composer require symfony/http-client
First Use Case: Fetching a URL
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\HttpClient\HttpClient;
// Basic synchronous request
$client = HttpClient::create();
$response = $client->request('GET', 'https://api.example.com/data');
$content = $response->toArray(); // Convert response to array
Laravel Integration (Service Provider)
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->singleton(HttpClientInterface::class, function () {
return HttpClient::create([
'base_uri' => 'https://api.example.com/',
'timeout' => 30,
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer ' . $this->app['auth']->user()->token,
],
]);
});
}
Accessing the Client in Controllers
use Symfony\Contracts\HttpClient\HttpClientInterface;
public function __construct(private HttpClientInterface $httpClient) {}
public function fetchData()
{
$response = $this->httpClient->request('GET', '/endpoint');
return response()->json($response->toArray());
}
Query Parameters & Headers
$response = $client->request('GET', '/search', [
'query' => [
'q' => 'laravel',
'page' => 1,
],
'headers' => [
'X-Custom-Header' => 'value',
],
]);
Authentication
// Basic Auth
$client->authenticate('username', 'password', 'basic');
// Bearer Token
$client->setDefaultOption('auth_bearer', 'your-token-here');
use Symfony\Contracts\HttpClient\ResponseStream;
// Fire-and-forget
$client->request('GET', '/async-endpoint');
// Stream responses
$responseStream = $client->stream('GET', '/large-file');
foreach ($responseStream as $chunk => $chunkResponse) {
file_put_contents('file.part', $chunkResponse->getContent());
}
Caching Responses
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Component\HttpClient\CachingHttpClient;
$cache = $this->app->make(CacheInterface::class);
$cachedClient = new CachingHttpClient(
$client,
$cache,
'http_cache_key_%s',
3600 // TTL in seconds
);
Retry Mechanism
use Symfony\Component\HttpClient\RetryHttpClient;
$retryClient = new RetryHttpClient(
$client,
[
'max_retries' => 3,
'delay' => 100, // ms
'max_delay' => 1000, // ms
'statuses' => [500, 502, 503, 504],
]
);
$response = $client->request('POST', '/upload', [
'body' => fopen('large-file.zip', 'r'),
'headers' => [
'Content-Type' => 'application/zip',
],
]);
use Symfony\Component\HttpClient\Middleware\MiddlewareInterface;
class LoggingMiddleware implements MiddlewareInterface
{
public function handle(Request $request, callable $next, callable $first = null)
{
\Log::info('Request:', [
'method' => $request->getMethod(),
'uri' => (string) $request->getUri(),
]);
$response = $next($request);
\Log::info('Response:', [
'status' => $response->getStatusCode(),
'headers' => $response->getHeaders(),
]);
return $response;
}
}
// Apply middleware
$client = HttpClient::create(['middleware' => [new LoggingMiddleware()]]);
// Follow redirects (default: 5)
$client->request('GET', '/redirect-me', [
'max_redirections' => 10,
]);
// Disable redirects
$client->request('GET', '/no-redirects', [
'follow_redirects' => false,
]);
$client = HttpClient::create([
'proxy' => [
'http' => 'tcp://proxy.example.com:8080',
'https' => 'tcp://proxy.example.com:8080',
],
]);
Resource Leaks with Streams
finally blocks.finally or try-catch-finally to close streams:
$response = $client->request('GET', '/stream-endpoint');
$stream = $response->getContent(false); // Get stream without consuming it
try {
// Process stream
} finally {
$stream->close();
}
Timeout Handling
null (infinite). Set explicit timeouts to avoid hanging requests.$client->request('GET', '/slow-endpoint', [
'timeout' => 10, // 10 seconds
]);
Connection Pooling Issues
max_host_connections to limit connections per host:
$client = HttpClient::create([
'max_host_connections' => 10,
]);
Caching Headers Conflicts
CachingHttpClient may not work as expected if responses lack proper Cache-Control or ETag headers.Async Response Handling
$promise = $client->request('GET', '/async-endpoint');
$response = $promise->wait(); // Blocking wait
// OR
$promise->then(function (ResponseInterface $response) {
// Handle response
});
SSL/TLS Issues
$client = HttpClient::create([
'verify_peer' => true,
'verify_host' => true,
'cafile' => __DIR__.'/custom-ca-bundle.crt',
]);
Decorators Order Matters
CachingHttpClient, RetryHttpClient) affects behavior.Enable Verbose Logging
$client = HttpClient::create([
'debug' => true,
]);
Inspect Raw Responses
$response = $client->request('GET', '/endpoint');
$content = $response->getContent(); // Raw body
$headers = $response->getHeaders(); // All headers
$status = $response->getStatusCode(); // HTTP status
Use copyAsCurl() for cURL Debugging
$request = $client->createRequest('GET', '/endpoint');
$curlCommand = $request->copyAsCurl();
\Log::info('cURL command:', [$curlCommand]);
Handle Exceptions Gracefully
try {
$response = $client->request('GET', '/endpoint');
} catch (TransportExceptionInterface $e) {
\Log::error('HTTP transport error:', ['exception' => $e]);
throw new \RuntimeException('Failed to fetch data', 0, $e);
} catch (ClientExceptionInterface $e) {
\Log::error('HTTP client error:', ['status' => $e->getStatusCode()]);
}
HttpClientInterface for protocol-specific clients (e.g., Guzzle, Buzzle).use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\RequestInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
class CustomHttpClient implements HttpClientInterface
{
public function request
How can I help you explore Laravel packages today?