guzzlehttp/command
Build higher-level web service clients on top of Guzzle by modeling operations as Commands and responses as Results. Includes a generic ServiceClient plus command middleware to map commands to PSR-7 requests and responses to structured results.
Installation:
composer require guzzlehttp/command
Basic Client Initialization:
use GuzzleHttp\Client;
use GuzzleHttp\Command\ServiceClient;
$httpClient = new Client(['base_uri' => 'https://api.example.com']);
$serviceClient = new ServiceClient(
$httpClient,
fn($command) => new Request('GET', '/api/' . $command->getName()),
fn($response) => new Result(json_decode($response->getBody(), true))
);
First Use Case: Execute a command with magic methods:
$result = $serviceClient->users(['limit' => 10]);
// Access result data via array syntax
$users = $result['data'];
Command Creation & Execution:
// Explicit command
$command = $client->getCommand('users', ['limit' => 10]);
$result = $client->execute($command);
// Magic method shortcut
$result = $client->users(['limit' => 10]);
Request Customization:
Use @http for per-command Guzzle options:
$result = $client->users([
'limit' => 10,
'@http' => ['timeout' => 5.0]
]);
Asynchronous Execution:
$promise = $client->usersAsync(['limit' => 10]);
$promise->then(fn($result) => $this->handleResult($result));
Dependency Injection: Bind ServiceClient in Laravel's container:
$this->app->bind(ServiceClient::class, fn() => new ServiceClient(
new Client(['base_uri' => config('services.api.base_uri')]),
// ... transformers
));
API Resource Mapping: Create a facade or service class:
class UserService {
public function __construct(private ServiceClient $client) {}
public function fetch(int $limit): array {
return $this->client->users(['limit' => $limit])['data'];
}
}
Middleware Stack: Add command-level middleware:
$client->getHandlerStack()->push(function($handler) {
return function($command) use ($handler) {
$command['@metadata']['timestamp'] = now();
return $handler($command);
};
});
Reserved @http Key:
@http.if (array_key_exists('@http', $input)) {
throw new \InvalidArgumentException('Forbidden key: @http');
}
Concurrency Limits:
$results = $client->executeAll($commands, ['concurrency' => 5]);
Result Parsing:
function($response) {
$body = (string)$response->getBody();
return new Result(json_decode($body, true) ?: []);
}
Enable Guzzle Debugging:
$client = new Client(['debug' => true]);
Inspect $response->getHeaders() for raw HTTP details.
Middleware Debugging:
Use tap() for debugging middleware:
$client->getHandlerStack()->push(function($handler) {
return function($command) use ($handler) {
tap($command, fn($c) => logger()->debug('Command:', $c->toArray()));
return $handler($command);
};
});
Custom Results:
Extend GuzzleHttp\Command\Result for typed responses:
class UserResult extends Result {
public function __construct(array $data) {
parent::__construct($data);
}
public function getIds(): array {
return array_column($this->toArray(), 'id');
}
}
Dynamic Command Mapping:
Use Laravel's macro to add dynamic methods:
ServiceClient::macro('fetchUser', function($id) {
return $this->users(['filter' => ['id' => $id]]);
});
Error Handling: Catch specific exceptions:
try {
$result = $client->users();
} catch (\GuzzleHttp\Command\Exception\CommandClientException $e) {
// Handle 4xx errors
} catch (\GuzzleHttp\Command\Exception\CommandServerException $e) {
// Handle 5xx errors
}
Config Integration:
Store API base URI in config/services.php:
'api' => [
'base_uri' => env('API_BASE_URI'),
],
Then inject via:
new Client(['base_uri' => config('services.api.base_uri')])
Exception Handling:
Use Laravel's exception handler to convert CommandException to HTTP responses:
public function render($request, CommandException $exception) {
return response()->json(['error' => $exception->getMessage()], 400);
}
Rate Limiting:
Combine with spatie/laravel-rate-limiting:
RateLimiter::for('api')->byRequest()->allow(100)->every(60);
How can I help you explore Laravel packages today?