Skip to main content

Installation

Install the package via Composer:
composer require cognesy/instructor-http-client
# @doctest id="947f"
You also need at least one supported HTTP library. The default driver uses PHP’s built-in cURL extension, so if cURL is available you can start immediately. For other drivers, install the corresponding package:
# Guzzle
composer require guzzlehttp/guzzle

# Symfony HttpClient
composer require symfony/http-client
# @doctest id="8511"

Requirements

  • PHP 8.2 or higher
  • JSON extension
  • cURL extension (included by default in most PHP installations)

Sending Your First Request

Using the HTTP client involves three steps: create a client, build a request, and read the response.
use Cognesy\Http\Data\HttpRequest;
use Cognesy\Http\HttpClient;

$client = HttpClient::default();

$response = $client->send(new HttpRequest(
    url: 'https://api.example.com/health',
    method: 'GET',
    headers: ['Accept' => 'application/json'],
    body: '',
    options: [],
))->get();

echo $response->statusCode(); // 200
echo $response->body();       // {"status":"ok"}
// @doctest id="7679"
HttpClient::default() creates a client with the default cURL driver. The send() method returns a PendingHttpResponse, which is lazy — the network call does not happen until you call get() or stream().

Choosing a Driver

If you want a specific driver, use a preset name or pass an HttpClientConfig:
use Cognesy\Http\HttpClient;

$client = HttpClient::using('guzzle');
// @doctest id="e1d7"
Or construct the config explicitly:
use Cognesy\Http\Config\HttpClientConfig;
use Cognesy\Http\HttpClient;

$client = HttpClient::fromConfig(new HttpClientConfig(driver: 'guzzle'));
// @doctest id="d03e"
Or use the builder for more control:
use Cognesy\Http\Config\HttpClientConfig;
use Cognesy\Http\Creation\HttpClientBuilder;

$client = (new HttpClientBuilder())
    ->withConfig(new HttpClientConfig(
        driver: 'symfony',
        connectTimeout: 5,
        requestTimeout: 30,
    ))
    ->create();
// @doctest id="5875"

Error Handling

HTTP requests can fail for many reasons. Wrap your calls in a try-catch block:
use Cognesy\Http\Exceptions\HttpRequestException;

try {
    $response = $client->send($request)->get();
} catch (HttpRequestException $e) {
    echo "Request failed: {$e->getMessage()}\n";

    if ($e->getResponse()) {
        echo "Status: {$e->getResponse()->statusCode()}\n";
    }
}
// @doctest id="b843"
The exception hierarchy gives you granular control:
ExceptionWhen
HttpRequestExceptionBase class for all HTTP errors
NetworkExceptionNetwork-level failures
ConnectionExceptionCould not connect to the host
TimeoutExceptionConnect or request timeout exceeded
HttpClientErrorExceptionHTTP 4xx response (when failOnError is true)
ServerErrorExceptionHTTP 5xx response (when failOnError is true)
CircuitBreakerOpenExceptionCircuit breaker is open for the target host
When failOnError is set to true in the config, the client throws typed exceptions for 4xx and 5xx responses automatically. When it is false (the default), you need to check the status code yourself.

Testing with Mocks

For tests, use the builder’s withMock() method to supply predefined responses without making real HTTP calls:
use Cognesy\Http\Creation\HttpClientBuilder;
use Cognesy\Http\Data\HttpResponse;

$client = (new HttpClientBuilder())
    ->withMock(function ($mock) {
        $mock->addResponse(
            HttpResponse::sync(200, ['Content-Type' => 'application/json'], '{"ok":true}'),
            url: 'https://api.example.com/health',
            method: 'GET',
        );
    })
    ->create();

$response = $client->send(new HttpRequest(
    url: 'https://api.example.com/health',
    method: 'GET',
    headers: [],
    body: '',
    options: [],
))->get();

echo $response->body(); // {"ok":true}
// @doctest id="0240"
The mock driver matches responses by URL and method, making it straightforward to verify that your application sends the right requests.

What’s Next

Now that you have a working client, explore the rest of the documentation: