> ## Documentation Index
> Fetch the complete documentation index at: https://docs.instructorphp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 2 getting started

## Installation

Install the package via Composer:

```bash theme={null}
composer require cognesy/instructor-http-client
# @doctest id="2c94"
```

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:

```bash theme={null}
# Guzzle
composer require guzzlehttp/guzzle

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

### 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.

```php theme={null}
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="a6c5"
```

`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`:

```php theme={null}
use Cognesy\Http\HttpClient;

$client = HttpClient::using('guzzle');
// @doctest id="e8eb"
```

Or construct the config explicitly:

```php theme={null}
use Cognesy\Http\Config\HttpClientConfig;
use Cognesy\Http\HttpClient;

$client = HttpClient::fromConfig(new HttpClientConfig(driver: 'guzzle'));
// @doctest id="3b96"
```

Or use the builder for more control:

```php theme={null}
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="3a79"
```

## Error Handling

HTTP requests can fail for many reasons. Wrap your calls in a try-catch block:

```php theme={null}
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="15d3"
```

The exception hierarchy gives you granular control:

| Exception                     | When                                           |
| ----------------------------- | ---------------------------------------------- |
| `HttpRequestException`        | Base class for all HTTP errors                 |
| `NetworkException`            | Network-level failures                         |
| `ConnectionException`         | Could not connect to the host                  |
| `TimeoutException`            | Connect or request timeout exceeded            |
| `HttpClientErrorException`    | HTTP 4xx response (when `failOnError` is true) |
| `ServerErrorException`        | HTTP 5xx response (when `failOnError` is true) |
| `CircuitBreakerOpenException` | Circuit 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:

```php theme={null}
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="e2bc"
```

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:

* [Making Requests](3-making-requests) -- learn about request construction, HTTP methods, headers, and bodies.
* [Handling Responses](4-handling-responses) -- read buffered content, inspect headers, and decode JSON.
* [Streaming Responses](5-streaming-responses) -- consume chunked data as it arrives.
* [Middleware](10-middleware) -- add retry logic, circuit breakers, and custom behaviors.
