friendship ended with social-app. php is my new best friend

title: Making Requests description: Learn how to make HTTP requests using the Fetch HTTP package#

Making Requests#

This guide covers different ways to make HTTP requests using the Fetch HTTP package, including the helper functions and the object-oriented interface.

Using Helper Functions#

The simplest way to make requests is using the global helper functions.

The fetch() Function#

The fetch() function is the primary way to make HTTP requests:

// Simple GET request
$response = fetch('https://api.example.com/users');

// POST request with JSON data
$response = fetch('https://api.example.com/users', [
    'method' => 'POST',
    'json' => ['name' => 'John Doe', 'email' => 'john@example.com']
]);

HTTP Method Helpers#

For common HTTP methods, you can use the dedicated helper functions:

// GET request
$response = get('https://api.example.com/users');

// POST request
$response = post('https://api.example.com/users', [
    'name' => 'John Doe',
    'email' => 'john@example.com'
]);

// PUT request
$response = put('https://api.example.com/users/123', [
    'name' => 'John Smith',
    'email' => 'john.smith@example.com'
]);

// PATCH request
$response = patch('https://api.example.com/users/123', [
    'status' => 'active'
]);

// DELETE request
$response = delete('https://api.example.com/users/123');

Using the Object-Oriented Interface#

For more control and reusability, you can use the object-oriented interface.

Using the Client Class#

The Client class provides a high-level interface:

use Fetch\Http\Client;

$client = new Client();

// Make requests
$response = $client->get('https://api.example.com/users');
$response = $client->post('https://api.example.com/users', [
    'name' => 'John Doe',
    'email' => 'john@example.com'
]);

Using the ClientHandler Class#

The ClientHandler class offers a fluent, chainable API with more options:

use Fetch\Http\ClientHandler;

$handler = new ClientHandler();

// Configure and make a request
$response = $handler
    ->withHeaders([
        'Accept' => 'application/json',
        'X-API-Key' => 'your-api-key'
    ])
    ->withToken('your-oauth-token')
    ->get('https://api.example.com/users');

Factory Methods#

You can also use static factory methods to create preconfigured clients:

// Create with base URI
$client = ClientHandler::createWithBaseUri('https://api.example.com');
$response = $client->get('/users'); // Uses the base URI

// Create with custom Guzzle client
$guzzleClient = new \GuzzleHttp\Client([
    'timeout' => 30,
    'verify' => false, // Disable SSL verification (not recommended for production)
]);
$client = ClientHandler::createWithClient($guzzleClient);

Request Configuration Options#

Whether you use the helper functions or the object-oriented interface, you can configure various aspects of your requests.

Headers#

// Using fetch()
$response = fetch('https://api.example.com/users', [
    'headers' => [
        'Accept' => 'application/json',
        'User-Agent' => 'MyApp/1.0',
        'X-Custom-Header' => 'value'
    ]
]);

// Using ClientHandler
$response = ClientHandler::create()
    ->withHeader('Accept', 'application/json')
    ->withHeader('User-Agent', 'MyApp/1.0')
    ->withHeader('X-Custom-Header', 'value')
    ->get('https://api.example.com/users');

// Setting multiple headers at once
$response = ClientHandler::create()
    ->withHeaders([
        'Accept' => 'application/json',
        'User-Agent' => 'MyApp/1.0',
        'X-Custom-Header' => 'value'
    ])
    ->get('https://api.example.com/users');

Query Parameters#

// Using fetch()
$response = fetch('https://api.example.com/users', [
    'query' => [
        'page' => 1,
        'per_page' => 20,
        'sort' => 'created_at',
        'order' => 'desc'
    ]
]);

// Using get() with query params as second argument
$response = get('https://api.example.com/users', [
    'page' => 1,
    'per_page' => 20
]);

// Using ClientHandler
$response = ClientHandler::create()
    ->withQueryParameter('page', 1)
    ->withQueryParameter('per_page', 20)
    ->withQueryParameter('sort', 'created_at')
    ->withQueryParameter('order', 'desc')
    ->get('https://api.example.com/users');

// Setting multiple query parameters at once
$response = ClientHandler::create()
    ->withQueryParameters([
        'page' => 1,
        'per_page' => 20,
        'sort' => 'created_at',
        'order' => 'desc'
    ])
    ->get('https://api.example.com/users');

Request Body#

JSON#

// Using fetch()
$response = fetch('https://api.example.com/users', [
    'method' => 'POST',
    'json' => [
        'name' => 'John Doe',
        'email' => 'john@example.com',
        'roles' => ['editor', 'admin']
    ]
]);

// Using post() helper (arrays are automatically sent as JSON)
$response = post('https://api.example.com/users', [
    'name' => 'John Doe',
    'email' => 'john@example.com',
    'roles' => ['editor', 'admin']
]);

// Using ClientHandler
$response = ClientHandler::create()
    ->withJson([
        'name' => 'John Doe',
        'email' => 'john@example.com',
        'roles' => ['editor', 'admin']
    ])
    ->post('https://api.example.com/users');

Form Data#

// Using fetch()
$response = fetch('https://api.example.com/login', [
    'method' => 'POST',
    'form' => [
        'username' => 'johndoe',
        'password' => 'secret',
        'remember' => true
    ]
]);

// Using ClientHandler
$response = ClientHandler::create()
    ->withFormParams([
        'username' => 'johndoe',
        'password' => 'secret',
        'remember' => true
    ])
    ->post('https://api.example.com/login');

Multipart Form Data (File Uploads)#

// Using fetch()
$response = fetch('https://api.example.com/upload', [
    'method' => 'POST',
    'multipart' => [
        [
            'name' => 'file',
            'contents' => file_get_contents('/path/to/image.jpg'),
            'filename' => 'upload.jpg',
            'headers' => ['Content-Type' => 'image/jpeg']
        ],
        [
            'name' => 'description',
            'contents' => 'Profile picture'
        ]
    ]
]);

// Using ClientHandler
$response = ClientHandler::create()
    ->withMultipart([
        [
            'name' => 'file',
            'contents' => fopen('/path/to/image.jpg', 'r'),
            'filename' => 'upload.jpg',
        ],
        [
            'name' => 'description',
            'contents' => 'Profile picture'
        ]
    ])
    ->post('https://api.example.com/upload');

Raw Body#

// Using fetch()
$response = fetch('https://api.example.com/webhook', [
    'method' => 'POST',
    'body' => 'Raw request content',
    'headers' => ['Content-Type' => 'text/plain']
]);

// Using ClientHandler with string body
$response = ClientHandler::create()
    ->withBody('Raw request content', 'text/plain')
    ->post('https://api.example.com/webhook');

// Using ClientHandler with body and content type enum
use Fetch\Enum\ContentType;
$response = ClientHandler::create()
    ->withBody('<user><name>John</name></user>', ContentType::XML)
    ->post('https://api.example.com/users');

Timeouts#

// Using fetch()
$response = fetch('https://api.example.com/slow-resource', [
    'timeout' => 30 // 30 seconds
]);

// Using ClientHandler
$response = ClientHandler::create()
    ->timeout(30)
    ->get('https://api.example.com/slow-resource');

Retries#

// Using fetch()
$response = fetch('https://api.example.com/flaky-resource', [
    'retries' => 3,
    'retry_delay' => 100 // 100ms initial delay with exponential backoff
]);

// Using ClientHandler
$response = ClientHandler::create()
    ->retry(3, 100) // 3 retries with 100ms initial delay
    ->retryStatusCodes([408, 429, 500, 502, 503, 504]) // Customize retryable status codes
    ->retryExceptions(['GuzzleHttp\Exception\ConnectException']) // Customize retryable exceptions
    ->get('https://api.example.com/flaky-resource');

Base URI#

// Using fetch()
$response = fetch('/users', [
    'base_uri' => 'https://api.example.com'
]);

// Using ClientHandler
$client = ClientHandler::createWithBaseUri('https://api.example.com');
$response = $client->get('/users');
$anotherResponse = $client->get('/posts');

Working with Enums#

use Fetch\Enum\Method;
use Fetch\Enum\ContentType;

// Use method enum
$response = ClientHandler::create()
    ->request(Method::POST, 'https://api.example.com/users', [
        'name' => 'John Doe',
        'email' => 'john@example.com'
    ]);

// Use content type enum
$response = ClientHandler::create()
    ->withBody('{"name":"John Doe"}', ContentType::JSON)
    ->post('https://api.example.com/users');

Reusing Configuration#

One of the advantages of the object-oriented interface is the ability to reuse configuration.

Reusing a Client#

$client = ClientHandler::createWithBaseUri('https://api.example.com')
    ->withToken('your-oauth-token')
    ->withHeader('Accept', 'application/json');

// Use the same configuration for multiple requests
$users = $client->get('/users')->json();
$user = $client->get("/users/{$id}")->json();
$posts = $client->get('/posts')->json();

Cloning with Modified Options#

// Create a base client
$baseClient = ClientHandler::createWithBaseUri('https://api.example.com')
    ->withHeaders([
        'User-Agent' => 'MyApp/1.0',
        'Accept' => 'application/json'
    ]);

// Create a clone with auth for protected endpoints
$authClient = $baseClient->withClonedOptions([
    'headers' => [
        'Authorization' => 'Bearer ' . $token
    ]
]);

// Another clone with different timeout
$longTimeoutClient = $baseClient->withClonedOptions([
    'timeout' => 60
]);

// Use the clients
$publicData = $baseClient->get('/public-data')->json();
$privateData = $authClient->get('/private-data')->json();
$largeReport = $longTimeoutClient->get('/large-report')->json();

Setting Global Defaults#

// Set default options for all new ClientHandler instances
ClientHandler::setDefaultOptions([
    'timeout' => 15,
    'headers' => [
        'User-Agent' => 'MyApp/1.0',
        'Accept' => 'application/json'
    ]
]);

// Set up a global client configuration
fetch_client([
    'base_uri' => 'https://api.example.com',
    'headers' => [
        'User-Agent' => 'MyApp/1.0'
    ],
    'timeout' => 10
]);

// Now simple helper functions will use this configuration
$users = get('/users')->json();

Logging#

The Fetch PHP package supports PSR-3 logging:

use Monolog\Logger;
use Monolog\Handler\StreamHandler;

// Create a logger
$logger = new Logger('fetch');
$logger->pushHandler(new StreamHandler('path/to/your.log', Logger::DEBUG));

// Set the logger on the client
$client = fetch_client();
$client->setLogger($logger);

// Or set it on the handler
$handler = fetch_client()->getHandler();
$handler->setLogger($logger);

// Now all requests and responses will be logged
$response = get('https://api.example.com/users');

Creating Mock Responses (For Testing)#

use Fetch\Http\ClientHandler;

// Create a simple mock response
$mockResponse = ClientHandler::createMockResponse(
    statusCode: 200,
    headers: ['Content-Type' => 'application/json'],
    body: '{"name": "John", "email": "john@example.com"}'
);

// Create a JSON mock response
$mockJsonResponse = ClientHandler::createJsonResponse(
    data: ['name' => 'John', 'email' => 'john@example.com'],
    statusCode: 200
);

PSR-7 and PSR-18 Compatibility#

The package implements PSR-7 and PSR-18 interfaces, making it compatible with other PSR-7 HTTP clients and middleware:

// Create a PSR-7 request
use GuzzleHttp\Psr7\Request;
$request = new Request('GET', 'https://api.example.com/users', [
    'Accept' => 'application/json'
]);

// Use with any PSR-18 client
$psr18Client = new SomePsr18Client();
$response = $psr18Client->sendRequest($request);

// Our Client class is PSR-18 compatible
$client = new \Fetch\Http\Client();
$response = $client->sendRequest($request);

Error Handling#

try {
    $response = fetch('https://api.example.com/users/999');

    if ($response->failed()) {
        echo "Request failed with status: " . $response->status();
    }
} catch (\Fetch\Exceptions\NetworkException $e) {
    echo "Network error: " . $e->getMessage();
} catch (\Fetch\Exceptions\RequestException $e) {
    echo "Request error: " . $e->getMessage();
} catch (\Fetch\Exceptions\ClientException $e) {
    echo "Client error: " . $e->getMessage();
} catch (\Exception $e) {
    echo "Error: " . $e->getMessage();
}

Next Steps#