Skip to content

HttpClient

Ray Fung edited this page Feb 26, 2026 · 3 revisions

HttpClient

Razy's HttpClient provides a fluent, cURL-based HTTP client for making external API requests. It supports JSON/form/multipart encoding, authentication, retries, timeouts, and request/response hooks.


Table of Contents


Quick Start

use Razy\Http\HttpClient;



$response = HttpClient::create()

    ->baseUrl('https://api.example.com')

    ->withToken('my-api-key')

    ->asJson()

    ->get('/users');



$users = $response->json();

Creating a Client

// Static factory

$client = HttpClient::create();



// Pre-configured for an API

$api = HttpClient::create()

    ->baseUrl('https://api.example.com/v2')

    ->withToken('secret-token')

    ->asJson()

    ->timeout(10);

All configuration methods return $this for fluent chaining.


Configuration

Base URL

$client->baseUrl('https://api.example.com/v2');



// Requests use relative paths

$client->get('/users');     // → https://api.example.com/v2/users

$client->get('/users/42');  // → https://api.example.com/v2/users/42

Headers

// Set multiple headers

$client->withHeaders([

    'Accept'       => 'application/json',

    'X-Request-Id' => 'abc-123',

]);



// Set a single header

$client->withHeader('X-Custom', 'value');

Authentication

// Bearer token

$client->withToken('eyJhbGciOiJIUzI1NiIs...');

// → Authorization: Bearer eyJhbGciOiJIUzI1NiIs...



// Basic auth

$client->withBasicAuth('username', 'password');

// → Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

Timeouts

$client->timeout(30);          // Total request timeout (seconds)

$client->connectTimeout(5);    // Connection timeout (seconds)

SSL Verification

// Disable SSL verification (development only!)

$client->withoutVerifying();

Content Types

// JSON (Content-Type: application/json)

$client->asJson();



// Form URL-encoded (Content-Type: application/x-www-form-urlencoded)

$client->asForm();



// Multipart (Content-Type: multipart/form-data)

$client->asMultipart();

Query Parameters

// Append query parameters to all requests

$client->withQuery([

    'api_key' => 'abc123',

    'format'  => 'json',

]);



// Combined with path: /users?api_key=abc123&format=json

$client->get('/users');

User Agent

$client->userAgent('MyApp/1.0');

Retries

// Retry up to 3 times with 1000ms delay between attempts

$client->retry(times: 3, sleepMs: 1000);

Retries are triggered on connection failures and 5xx server errors.

cURL Options

// Set arbitrary cURL options

$client->withCurlOption(CURLOPT_FOLLOWLOCATION, true);

$client->withCurlOption(CURLOPT_MAXREDIRS, 5);

Making Requests

GET

$response = $client->get('/users');

$response = $client->get('/users', ['role' => 'admin']);  // query params

POST

// JSON body (if asJson() is set)

$response = $client->post('/users', [

    'name'  => 'Alice',

    'email' => 'alice@example.com',

]);



// Form body (if asForm() is set)

$response = $client->asForm()->post('/login', [

    'username' => 'alice',

    'password' => 'secret',

]);



// Multipart file upload

$response = $client->asMultipart()->post('/upload', [

    'file' => new \CURLFile('/path/to/photo.jpg', 'image/jpeg', 'photo.jpg'),

    'description' => 'Profile photo',

]);

PUT / PATCH / DELETE

$response = $client->put('/users/42', ['name' => 'Alice Updated']);

$response = $client->patch('/users/42', ['name' => 'Alice Patched']);

$response = $client->delete('/users/42');

HEAD / OPTIONS

$response = $client->head('/health');

$response = $client->options('/users');

Generic Send

$response = $client->send('GET', '/custom-endpoint', [

    'key' => 'value',

]);

Handling Responses

All HTTP methods return an HttpResponse object.

Status Checks

$response->status();         // 200



// Boolean helpers

$response->successful();     // 2xx

$response->ok();             // 200

$response->failed();         // 4xx or 5xx

$response->redirect();       // 3xx

$response->clientError();    // 4xx

$response->serverError();    // 5xx

Body & JSON

// Raw body

$html = $response->body();



// Decode JSON

$data = $response->json();

// ['users' => [['id' => 1, 'name' => 'Alice'], ...]]



// Dot-notation access into JSON

$name = $response->jsonGet('users.0.name');

// 'Alice'

Response Headers

// All headers

$headers = $response->headers();



// Single header

$type = $response->header('Content-Type');

// 'application/json; charset=utf-8'



// Check if header exists

$response->hasHeader('X-Rate-Limit');



// Content type shorthand

$response->contentType();

// 'application/json; charset=utf-8'

Error Handling

// Throw HttpException on failure (4xx/5xx)

$response->throw();



// Conditional throw

$response->throwIf($response->status() === 422);



// The exception wraps the response object

try {

    $response->throw();

} catch (\Razy\Http\HttpException $e) {

    echo $e->getCode();       // HTTP status code (e.g., 404)

    echo $e->getMessage();    // Error message

    $resp = $e->response;     // Original HttpResponse

    $body = $resp->json();    // Access response body

}

Serialisation

$array = $response->toArray();

// ['status' => 200, 'headers' => [...], 'body' => '...']

Hooks

Register callbacks for request/response lifecycle:

$client->beforeSending(function (array &$options) {

    // Modify cURL options before the request

    $options[CURLOPT_VERBOSE] = true;

    

    // Log outgoing request

    Log::debug('HTTP Request', $options);

});



$client->afterResponse(function (HttpResponse $response) {

    // Log response

    Log::debug('HTTP Response', [

        'status' => $response->status(),

        'body'   => substr($response->body(), 0, 500),

    ]);

    

    // Rate limit tracking

    if ($response->hasHeader('X-Rate-Limit-Remaining')) {

        RateTracker::update((int) $response->header('X-Rate-Limit-Remaining'));

    }

});

Full Example

REST API Client

use Razy\Http\HttpClient;



class GitHubClient

{

    private HttpClient $http;



    public function __construct(string $token)

    {

        $this->http = HttpClient::create()

            ->baseUrl('https://api.github.com')

            ->withToken($token)

            ->asJson()

            ->withHeaders([

                'Accept'               => 'application/vnd.github.v3+json',

                'X-GitHub-Api-Version' => '2022-11-28',

            ])

            ->timeout(15)

            ->retry(times: 2, sleepMs: 1000);

    }



    public function getUser(string $username): array

    {

        return $this->http

            ->get("/users/{$username}")

            ->throw()

            ->json();

    }



    public function listRepos(string $username, int $page = 1): array

    {

        return $this->http

            ->get("/users/{$username}/repos", [

                'page'     => $page,

                'per_page' => 30,

                'sort'     => 'updated',

            ])

            ->throw()

            ->json();

    }



    public function createIssue(string $owner, string $repo, string $title, string $body): array

    {

        return $this->http

            ->post("/repos/{$owner}/{$repo}/issues", [

                'title' => $title,

                'body'  => $body,

            ])

            ->throw()

            ->json();

    }

}



// Usage

$github = new GitHubClient('ghp_xxxxxxxxxxxx');

$user = $github->getUser('octocat');

$repos = $github->listRepos('octocat');

API Reference

HttpClient

| Method | Signature | Returns |

| --- | --- | --- |

| create | (): static | New instance |

| baseUrl | (string $url): static | Set base URL |

| withHeaders | (array $headers): static | Set multiple headers |

| withHeader | (string $name, string $value): static | Set single header |

| withToken | (string $token): static | Bearer auth |

| withBasicAuth | (string $user, string $pass): static | Basic auth |

| timeout | (int $seconds): static | Request timeout |

| connectTimeout | (int $seconds): static | Connect timeout |

| withoutVerifying | (): static | Disable SSL verify |

| asJson | (): static | JSON content type |

| asForm | (): static | Form content type |

| asMultipart | (): static | Multipart content type |

| withQuery | (array $params): static | Default query params |

| userAgent | (string $ua): static | User-Agent header |

| retry | (int $times, int $sleepMs): static | Retry config |

| withCurlOption | (int $opt, mixed $val): static | cURL option |

| beforeSending | (Closure $cb): static | Pre-request hook |

| afterResponse | (Closure $cb): static | Post-response hook |

| get | (string $url, array $query = []): HttpResponse | GET request |

| post | (string $url, array $data = []): HttpResponse | POST request |

| put | (string $url, array $data = []): HttpResponse | PUT request |

| patch | (string $url, array $data = []): HttpResponse | PATCH request |

| delete | (string $url, array $data = []): HttpResponse | DELETE request |

| head | (string $url): HttpResponse | HEAD request |

| options | (string $url): HttpResponse | OPTIONS request |

| send | (string $method, string $url, array $data = []): HttpResponse | Generic request |

HttpResponse

| Method | Signature | Returns |

| --- | --- | --- |

| status | (): int | HTTP status code |

| successful | (): bool | 2xx? |

| ok | (): bool | 200? |

| failed | (): bool | 4xx or 5xx? |

| redirect | (): bool | 3xx? |

| clientError | (): bool | 4xx? |

| serverError | (): bool | 5xx? |

| body | (): string | Raw body |

| json | (): mixed | JSON decoded |

| jsonGet | (string $dotPath): mixed | Dot-notation access |

| headers | (): array | All headers |

| header | (string $name): ?string | Single header |

| hasHeader | (string $name): bool | Header exists? |

| contentType | (): ?string | Content-Type |

| throw | (): static | Throw on failure |

| throwIf | (bool $condition): static | Conditional throw |

| toArray | (): array | Serialise |

HttpException

| Property | Type | Description |

| --- | --- | --- |

| response | HttpResponse | Original response |

| Code | int | HTTP status code |

Extends \RuntimeException.

← Previous: Queue

Database

Clone this wiki locally