friendship ended with social-app. php is my new best friend
1--- 2title: Making Requests 3description: Learn how to make HTTP requests using the Fetch HTTP package 4--- 5 6# Making Requests 7 8This guide covers different ways to make HTTP requests using the Fetch HTTP package, including the helper functions and the object-oriented interface. 9 10## Using Helper Functions 11 12The simplest way to make requests is using the global helper functions. 13 14### The `fetch()` Function 15 16The `fetch()` function is the primary way to make HTTP requests: 17 18```php 19// Simple GET request 20$response = fetch('https://api.example.com/users'); 21 22// POST request with JSON data 23$response = fetch('https://api.example.com/users', [ 24 'method' => 'POST', 25 'json' => ['name' => 'John Doe', 'email' => 'john@example.com'] 26]); 27``` 28 29### HTTP Method Helpers 30 31For common HTTP methods, you can use the dedicated helper functions: 32 33```php 34// GET request 35$response = get('https://api.example.com/users'); 36 37// POST request 38$response = post('https://api.example.com/users', [ 39 'name' => 'John Doe', 40 'email' => 'john@example.com' 41]); 42 43// PUT request 44$response = put('https://api.example.com/users/123', [ 45 'name' => 'John Smith', 46 'email' => 'john.smith@example.com' 47]); 48 49// PATCH request 50$response = patch('https://api.example.com/users/123', [ 51 'status' => 'active' 52]); 53 54// DELETE request 55$response = delete('https://api.example.com/users/123'); 56``` 57 58## Using the Object-Oriented Interface 59 60For more control and reusability, you can use the object-oriented interface. 61 62### Using the Client Class 63 64The `Client` class provides a high-level interface: 65 66```php 67use Fetch\Http\Client; 68 69$client = new Client(); 70 71// Make requests 72$response = $client->get('https://api.example.com/users'); 73$response = $client->post('https://api.example.com/users', [ 74 'name' => 'John Doe', 75 'email' => 'john@example.com' 76]); 77``` 78 79### Using the ClientHandler Class 80 81The `ClientHandler` class offers a fluent, chainable API with more options: 82 83```php 84use Fetch\Http\ClientHandler; 85 86$handler = new ClientHandler(); 87 88// Configure and make a request 89$response = $handler 90 ->withHeaders([ 91 'Accept' => 'application/json', 92 'X-API-Key' => 'your-api-key' 93 ]) 94 ->withToken('your-oauth-token') 95 ->get('https://api.example.com/users'); 96``` 97 98### Factory Methods 99 100You can also use static factory methods to create preconfigured clients: 101 102```php 103// Create with base URI 104$client = ClientHandler::createWithBaseUri('https://api.example.com'); 105$response = $client->get('/users'); // Uses the base URI 106 107// Create with custom Guzzle client 108$guzzleClient = new \GuzzleHttp\Client([ 109 'timeout' => 30, 110 'verify' => false, // Disable SSL verification (not recommended for production) 111]); 112$client = ClientHandler::createWithClient($guzzleClient); 113``` 114 115## Request Configuration Options 116 117Whether you use the helper functions or the object-oriented interface, you can configure various aspects of your requests. 118 119### Headers 120 121```php 122// Using fetch() 123$response = fetch('https://api.example.com/users', [ 124 'headers' => [ 125 'Accept' => 'application/json', 126 'User-Agent' => 'MyApp/1.0', 127 'X-Custom-Header' => 'value' 128 ] 129]); 130 131// Using ClientHandler 132$response = ClientHandler::create() 133 ->withHeader('Accept', 'application/json') 134 ->withHeader('User-Agent', 'MyApp/1.0') 135 ->withHeader('X-Custom-Header', 'value') 136 ->get('https://api.example.com/users'); 137 138// Setting multiple headers at once 139$response = ClientHandler::create() 140 ->withHeaders([ 141 'Accept' => 'application/json', 142 'User-Agent' => 'MyApp/1.0', 143 'X-Custom-Header' => 'value' 144 ]) 145 ->get('https://api.example.com/users'); 146``` 147 148### Query Parameters 149 150```php 151// Using fetch() 152$response = fetch('https://api.example.com/users', [ 153 'query' => [ 154 'page' => 1, 155 'per_page' => 20, 156 'sort' => 'created_at', 157 'order' => 'desc' 158 ] 159]); 160 161// Using get() with query params as second argument 162$response = get('https://api.example.com/users', [ 163 'page' => 1, 164 'per_page' => 20 165]); 166 167// Using ClientHandler 168$response = ClientHandler::create() 169 ->withQueryParameter('page', 1) 170 ->withQueryParameter('per_page', 20) 171 ->withQueryParameter('sort', 'created_at') 172 ->withQueryParameter('order', 'desc') 173 ->get('https://api.example.com/users'); 174 175// Setting multiple query parameters at once 176$response = ClientHandler::create() 177 ->withQueryParameters([ 178 'page' => 1, 179 'per_page' => 20, 180 'sort' => 'created_at', 181 'order' => 'desc' 182 ]) 183 ->get('https://api.example.com/users'); 184``` 185 186### Request Body 187 188#### JSON 189 190```php 191// Using fetch() 192$response = fetch('https://api.example.com/users', [ 193 'method' => 'POST', 194 'json' => [ 195 'name' => 'John Doe', 196 'email' => 'john@example.com', 197 'roles' => ['editor', 'admin'] 198 ] 199]); 200 201// Using post() helper (arrays are automatically sent as JSON) 202$response = post('https://api.example.com/users', [ 203 'name' => 'John Doe', 204 'email' => 'john@example.com', 205 'roles' => ['editor', 'admin'] 206]); 207 208// Using ClientHandler 209$response = ClientHandler::create() 210 ->withJson([ 211 'name' => 'John Doe', 212 'email' => 'john@example.com', 213 'roles' => ['editor', 'admin'] 214 ]) 215 ->post('https://api.example.com/users'); 216``` 217 218#### Form Data 219 220```php 221// Using fetch() 222$response = fetch('https://api.example.com/login', [ 223 'method' => 'POST', 224 'form' => [ 225 'username' => 'johndoe', 226 'password' => 'secret', 227 'remember' => true 228 ] 229]); 230 231// Using ClientHandler 232$response = ClientHandler::create() 233 ->withFormParams([ 234 'username' => 'johndoe', 235 'password' => 'secret', 236 'remember' => true 237 ]) 238 ->post('https://api.example.com/login'); 239``` 240 241#### Multipart Form Data (File Uploads) 242 243```php 244// Using fetch() 245$response = fetch('https://api.example.com/upload', [ 246 'method' => 'POST', 247 'multipart' => [ 248 [ 249 'name' => 'file', 250 'contents' => file_get_contents('/path/to/image.jpg'), 251 'filename' => 'upload.jpg', 252 'headers' => ['Content-Type' => 'image/jpeg'] 253 ], 254 [ 255 'name' => 'description', 256 'contents' => 'Profile picture' 257 ] 258 ] 259]); 260 261// Using ClientHandler 262$response = ClientHandler::create() 263 ->withMultipart([ 264 [ 265 'name' => 'file', 266 'contents' => fopen('/path/to/image.jpg', 'r'), 267 'filename' => 'upload.jpg', 268 ], 269 [ 270 'name' => 'description', 271 'contents' => 'Profile picture' 272 ] 273 ]) 274 ->post('https://api.example.com/upload'); 275``` 276 277#### Raw Body 278 279```php 280// Using fetch() 281$response = fetch('https://api.example.com/webhook', [ 282 'method' => 'POST', 283 'body' => 'Raw request content', 284 'headers' => ['Content-Type' => 'text/plain'] 285]); 286 287// Using ClientHandler with string body 288$response = ClientHandler::create() 289 ->withBody('Raw request content', 'text/plain') 290 ->post('https://api.example.com/webhook'); 291 292// Using ClientHandler with body and content type enum 293use Fetch\Enum\ContentType; 294$response = ClientHandler::create() 295 ->withBody('<user><name>John</name></user>', ContentType::XML) 296 ->post('https://api.example.com/users'); 297``` 298 299### Timeouts 300 301```php 302// Using fetch() 303$response = fetch('https://api.example.com/slow-resource', [ 304 'timeout' => 30 // 30 seconds 305]); 306 307// Using ClientHandler 308$response = ClientHandler::create() 309 ->timeout(30) 310 ->get('https://api.example.com/slow-resource'); 311``` 312 313### Retries 314 315```php 316// Using fetch() 317$response = fetch('https://api.example.com/flaky-resource', [ 318 'retries' => 3, 319 'retry_delay' => 100 // 100ms initial delay with exponential backoff 320]); 321 322// Using ClientHandler 323$response = ClientHandler::create() 324 ->retry(3, 100) // 3 retries with 100ms initial delay 325 ->retryStatusCodes([408, 429, 500, 502, 503, 504]) // Customize retryable status codes 326 ->retryExceptions(['GuzzleHttp\Exception\ConnectException']) // Customize retryable exceptions 327 ->get('https://api.example.com/flaky-resource'); 328``` 329 330### Base URI 331 332```php 333// Using fetch() 334$response = fetch('/users', [ 335 'base_uri' => 'https://api.example.com' 336]); 337 338// Using ClientHandler 339$client = ClientHandler::createWithBaseUri('https://api.example.com'); 340$response = $client->get('/users'); 341$anotherResponse = $client->get('/posts'); 342``` 343 344### Working with Enums 345 346```php 347use Fetch\Enum\Method; 348use Fetch\Enum\ContentType; 349 350// Use method enum 351$response = ClientHandler::create() 352 ->request(Method::POST, 'https://api.example.com/users', [ 353 'name' => 'John Doe', 354 'email' => 'john@example.com' 355 ]); 356 357// Use content type enum 358$response = ClientHandler::create() 359 ->withBody('{"name":"John Doe"}', ContentType::JSON) 360 ->post('https://api.example.com/users'); 361``` 362 363## Reusing Configuration 364 365One of the advantages of the object-oriented interface is the ability to reuse configuration. 366 367### Reusing a Client 368 369```php 370$client = ClientHandler::createWithBaseUri('https://api.example.com') 371 ->withToken('your-oauth-token') 372 ->withHeader('Accept', 'application/json'); 373 374// Use the same configuration for multiple requests 375$users = $client->get('/users')->json(); 376$user = $client->get("/users/{$id}")->json(); 377$posts = $client->get('/posts')->json(); 378``` 379 380### Cloning with Modified Options 381 382```php 383// Create a base client 384$baseClient = ClientHandler::createWithBaseUri('https://api.example.com') 385 ->withHeaders([ 386 'User-Agent' => 'MyApp/1.0', 387 'Accept' => 'application/json' 388 ]); 389 390// Create a clone with auth for protected endpoints 391$authClient = $baseClient->withClonedOptions([ 392 'headers' => [ 393 'Authorization' => 'Bearer ' . $token 394 ] 395]); 396 397// Another clone with different timeout 398$longTimeoutClient = $baseClient->withClonedOptions([ 399 'timeout' => 60 400]); 401 402// Use the clients 403$publicData = $baseClient->get('/public-data')->json(); 404$privateData = $authClient->get('/private-data')->json(); 405$largeReport = $longTimeoutClient->get('/large-report')->json(); 406``` 407 408## Setting Global Defaults 409 410```php 411// Set default options for all new ClientHandler instances 412ClientHandler::setDefaultOptions([ 413 'timeout' => 15, 414 'headers' => [ 415 'User-Agent' => 'MyApp/1.0', 416 'Accept' => 'application/json' 417 ] 418]); 419 420// Set up a global client configuration 421fetch_client([ 422 'base_uri' => 'https://api.example.com', 423 'headers' => [ 424 'User-Agent' => 'MyApp/1.0' 425 ], 426 'timeout' => 10 427]); 428 429// Now simple helper functions will use this configuration 430$users = get('/users')->json(); 431``` 432 433## Logging 434 435The Fetch PHP package supports PSR-3 logging: 436 437```php 438use Monolog\Logger; 439use Monolog\Handler\StreamHandler; 440 441// Create a logger 442$logger = new Logger('fetch'); 443$logger->pushHandler(new StreamHandler('path/to/your.log', Logger::DEBUG)); 444 445// Set the logger on the client 446$client = fetch_client(); 447$client->setLogger($logger); 448 449// Or set it on the handler 450$handler = fetch_client()->getHandler(); 451$handler->setLogger($logger); 452 453// Now all requests and responses will be logged 454$response = get('https://api.example.com/users'); 455``` 456 457## Creating Mock Responses (For Testing) 458 459```php 460use Fetch\Http\ClientHandler; 461 462// Create a simple mock response 463$mockResponse = ClientHandler::createMockResponse( 464 statusCode: 200, 465 headers: ['Content-Type' => 'application/json'], 466 body: '{"name": "John", "email": "john@example.com"}' 467); 468 469// Create a JSON mock response 470$mockJsonResponse = ClientHandler::createJsonResponse( 471 data: ['name' => 'John', 'email' => 'john@example.com'], 472 statusCode: 200 473); 474``` 475 476## PSR-7 and PSR-18 Compatibility 477 478The package implements PSR-7 and PSR-18 interfaces, making it compatible with other PSR-7 HTTP clients and middleware: 479 480```php 481// Create a PSR-7 request 482use GuzzleHttp\Psr7\Request; 483$request = new Request('GET', 'https://api.example.com/users', [ 484 'Accept' => 'application/json' 485]); 486 487// Use with any PSR-18 client 488$psr18Client = new SomePsr18Client(); 489$response = $psr18Client->sendRequest($request); 490 491// Our Client class is PSR-18 compatible 492$client = new \Fetch\Http\Client(); 493$response = $client->sendRequest($request); 494``` 495 496## Error Handling 497 498```php 499try { 500 $response = fetch('https://api.example.com/users/999'); 501 502 if ($response->failed()) { 503 echo "Request failed with status: " . $response->status(); 504 } 505} catch (\Fetch\Exceptions\NetworkException $e) { 506 echo "Network error: " . $e->getMessage(); 507} catch (\Fetch\Exceptions\RequestException $e) { 508 echo "Request error: " . $e->getMessage(); 509} catch (\Fetch\Exceptions\ClientException $e) { 510 echo "Client error: " . $e->getMessage(); 511} catch (\Exception $e) { 512 echo "Error: " . $e->getMessage(); 513} 514``` 515 516## Next Steps 517 518- Learn more about [Working with Responses](/guide/working-with-responses) 519- Explore [Authentication](/guide/authentication) options 520- Discover [Asynchronous Requests](/guide/async-requests) 521- Learn about [Working with Enums](/guide/working-with-enums) for type-safe HTTP operations