160 lines
3.6 KiB
Markdown
160 lines
3.6 KiB
Markdown
# HTTP Client Best Practices
|
|
|
|
## Always Set Explicit Timeouts
|
|
|
|
The default timeout is 30 seconds — too long for most API calls. Always set explicit `timeout` and `connectTimeout` to fail fast.
|
|
|
|
Incorrect:
|
|
```php
|
|
$response = Http::get('https://api.example.com/users');
|
|
```
|
|
|
|
Correct:
|
|
```php
|
|
$response = Http::timeout(5)
|
|
->connectTimeout(3)
|
|
->get('https://api.example.com/users');
|
|
```
|
|
|
|
For service-specific clients, define timeouts in a macro:
|
|
|
|
```php
|
|
Http::macro('github', function () {
|
|
return Http::baseUrl('https://api.github.com')
|
|
->timeout(10)
|
|
->connectTimeout(3)
|
|
->withToken(config('services.github.token'));
|
|
});
|
|
|
|
$response = Http::github()->get('/repos/laravel/framework');
|
|
```
|
|
|
|
## Use Retry with Backoff for External APIs
|
|
|
|
External APIs have transient failures. Use `retry()` with increasing delays.
|
|
|
|
Incorrect:
|
|
```php
|
|
$response = Http::post('https://api.stripe.com/v1/charges', $data);
|
|
|
|
if ($response->failed()) {
|
|
throw new PaymentFailedException('Charge failed');
|
|
}
|
|
```
|
|
|
|
Correct:
|
|
```php
|
|
$response = Http::retry([100, 500, 1000])
|
|
->timeout(10)
|
|
->post('https://api.stripe.com/v1/charges', $data);
|
|
```
|
|
|
|
Only retry on specific errors:
|
|
|
|
```php
|
|
$response = Http::retry(3, 100, function (Exception $exception, PendingRequest $request) {
|
|
return $exception instanceof ConnectionException
|
|
|| ($exception instanceof RequestException && $exception->response->serverError());
|
|
})->post('https://api.example.com/data');
|
|
```
|
|
|
|
## Handle Errors Explicitly
|
|
|
|
The HTTP Client does not throw on 4xx/5xx by default. Always check status or use `throw()`.
|
|
|
|
Incorrect:
|
|
```php
|
|
$response = Http::get('https://api.example.com/users/1');
|
|
$user = $response->json(); // Could be an error body
|
|
```
|
|
|
|
Correct:
|
|
```php
|
|
$response = Http::timeout(5)
|
|
->get('https://api.example.com/users/1')
|
|
->throw();
|
|
|
|
$user = $response->json();
|
|
```
|
|
|
|
For graceful degradation:
|
|
|
|
```php
|
|
$response = Http::get('https://api.example.com/users/1');
|
|
|
|
if ($response->successful()) {
|
|
return $response->json();
|
|
}
|
|
|
|
if ($response->notFound()) {
|
|
return null;
|
|
}
|
|
|
|
$response->throw();
|
|
```
|
|
|
|
## Use Request Pooling for Concurrent Requests
|
|
|
|
When making multiple independent API calls, use `Http::pool()` instead of sequential calls.
|
|
|
|
Incorrect:
|
|
```php
|
|
$users = Http::get('https://api.example.com/users')->json();
|
|
$posts = Http::get('https://api.example.com/posts')->json();
|
|
$comments = Http::get('https://api.example.com/comments')->json();
|
|
```
|
|
|
|
Correct:
|
|
```php
|
|
use Illuminate\Http\Client\Pool;
|
|
|
|
$responses = Http::pool(fn (Pool $pool) => [
|
|
$pool->as('users')->get('https://api.example.com/users'),
|
|
$pool->as('posts')->get('https://api.example.com/posts'),
|
|
$pool->as('comments')->get('https://api.example.com/comments'),
|
|
]);
|
|
|
|
$users = $responses['users']->json();
|
|
$posts = $responses['posts']->json();
|
|
```
|
|
|
|
## Fake HTTP Calls in Tests
|
|
|
|
Never make real HTTP requests in tests. Use `Http::fake()` and `preventStrayRequests()`.
|
|
|
|
Incorrect:
|
|
```php
|
|
it('syncs user from API', function () {
|
|
$service = new UserSyncService;
|
|
$service->sync(1); // Hits the real API
|
|
});
|
|
```
|
|
|
|
Correct:
|
|
```php
|
|
it('syncs user from API', function () {
|
|
Http::preventStrayRequests();
|
|
|
|
Http::fake([
|
|
'api.example.com/users/1' => Http::response([
|
|
'name' => 'John Doe',
|
|
'email' => 'john@example.com',
|
|
]),
|
|
]);
|
|
|
|
$service = new UserSyncService;
|
|
$service->sync(1);
|
|
|
|
Http::assertSent(function (Request $request) {
|
|
return $request->url() === 'https://api.example.com/users/1';
|
|
});
|
|
});
|
|
```
|
|
|
|
Test failure scenarios too:
|
|
|
|
```php
|
|
Http::fake([
|
|
'api.example.com/*' => Http::failedConnection(),
|
|
]);
|
|
``` |