【Laravel】HTTPクライアント(Http Facade)の使い方|外部API呼び出し・モック・リトライ設定
Laravel の Http Facade を使うと、外部 API 呼び出しを短いチェーンで書き、テストでは Http::fake で差し替えられます。本記事では基本操作からリトライ・モックまで整理します。
細かな API は Laravel のメジャーバージョンで変わることがあります。手元の composer.json の laravel/framework に合わせ、Laravel 公式ドキュメントの HTTP Client 章を確認してください。
はじめに
決済 API や社内マイクロサービス、SaaS の Webhook 先など、Laravel アプリから外部 HTTP を呼ぶ場面は多いです。file_get_contents や curl をその都度書くと、タイムアウトや認証、JSON の扱いが場所ごとに異なり、テストも外部通信に依存しがちになります。
Http Facade を使うと、リクエストの組み立てとレスポンスの確認を Laravel 流の API で統一できます。テストでは Http::fake でスタブを返せるため、本番 API に依存しない検証もしやすくなります。
この記事では次の範囲を扱います。
- 基本的な GET / POST とレスポンスの読み方
- ヘッダー・認証・タイムアウトの指定
- エラー時の
throw()とログ retryによる自動リトライHttp::fakeを使ったテスト- 実務でハマりやすい失敗例
背景:Http Facade が担う役割
Laravel の HTTP Client は、PHP で広く使われる HTTP クライアント Guzzle をベースにしたラッパーです。Http は Facade の一種で、static 風に書きながら裏側では HTTP クライアントへ処理を委譲します。
Illuminate\\Support\\Facades\\Http から get や post などを呼び出すと、Illuminate\\Http\\Client\\Response が返ります。
| やりたいこと | 主なメソッド |
|---|---|
| GET で JSON を取得 | Http::get(...) → $response->json() |
| POST で JSON を送る | Http::post($url, $data) |
| 認証ヘッダーを付ける | Http::withToken(...) / Http::withHeaders(...) |
| 待ち時間を制限する | Http::timeout(秒) |
| 失敗時に再試行する | Http::retry(回数, 待機ミリ秒) |
| テストで通信を差し替える | Http::fake([...]) |
Guzzle を直接使う選択肢もありますが、Laravel アプリ内では Http Facade のほうがドキュメントやテスト支援と揃いやすいです。
実装と検証
基本的な GET / POST
まずは最小の GET リクエストです。
<?php
use Illuminate\\Support\\Facades\\Http;
$response = Http::get('<https://api.example.com/users/1>');
if ($response->successful()) {
$name = $response->json('name');
}
POST で JSON を送る例です。post の第 2 引数は、デフォルトで application/json として送られます。
<?php
use Illuminate\\Support\\Facades\\Http;
$response = Http::post('<https://api.example.com/users>', [
'name' => 'Taylor',
'role' => 'Developer',
]);
if ($response->created()) {
$userId = $response->json('id');
}
レスポンスは body()、json()、status()、successful() などで確認できます。successful() はステータスコードが 200 番台かどうかを返します。
ヘッダーと Bearer トークン
API キーや Bearer トークンは、チェーンで指定します。
<?php
use Illuminate\\Support\\Facades\\Http;
$response = Http::withHeaders([
'Accept' => 'application/json',
])->withToken($accessToken)->get('<https://api.example.com/me>');
withToken は Authorization: Bearer ... ヘッダーを付与するショートカットです。acceptJson() は Accept: application/json を付ける糖衣メソッドです。
複数の外部 API を呼ぶサービスでは、ベース URL と共通ヘッダーを Macro にまとめる方法もあります。Macro は Http::github() のように、あらかじめ設定済みのクライアントを再利用する仕組みです。
タイムアウト
公式ドキュメントによると、HTTP クライアントのデフォルトタイムアウトは 30 秒です。接続待ちの上限(connectTimeout)は 10 秒が既定です。外部 API の応答が遅い場合は、明示的に短く設定するとよいです。
<?php
use Illuminate\\Support\\Facades\\Http;
$response = Http::timeout(5)
->connectTimeout(2)
->get('<https://api.example.com/slow-endpoint>');
タイムアウトを超えると Illuminate\\Http\\Client\\ConnectionException がスローされます。
エラー時の扱い
Laravel の HTTP クライアントは、Guzzle と異なり 400 番台・500 番台でも自動的に例外を投げません。失敗を検知したいときは $response->successful() を確認するか、$response->throw() を呼びます。
<?php
use Illuminate\\Support\\Facades\\Http;
use Illuminate\\Support\\Facades\\Log;
$response = Http::post('<https://api.example.com/orders>', ['item_id' => 42]);
if ($response->failed()) {
Log::warning('External API error', [
'status' => $response->status(),
'body' => $response->body(),
]);
}
// 失敗時は RequestException を投げたい場合
$data = Http::post('<https://api.example.com/orders>', ['item_id' => 42])
->throw()
->json();
throw() は 4xx / 5xx のとき RequestException を投げます。呼び出し側でステータスごとに分岐したい場合は、throw() を使わず successful() や status() で判定します。
リトライ設定
一時的な 5xx や接続エラーに備えて、retry で自動再試行できます。第 1 引数は最大試行回数、第 2 引数は試行間の待機ミリ秒です。既定では接続失敗や 4xx / 5xx などがリトライ対象になります。
<?php
use Illuminate\\Support\\Facades\\Http;
$response = Http::retry(3, 100)
->withToken($token)
->post('<https://api.example.com/orders>', [
'item_id' => 42,
'quantity' => 1,
]);
第 3 引数に callable を渡すと、「どの失敗でリトライするか」を絞れます。401 Unauthorized のように再試行しても意味がないケースでは、ここで条件を絞ります。第 2 引数の $request は、これから送るリクエストオブジェクト(PendingRequest)です。
<?php
use Illuminate\\Http\\Client\\ConnectionException;
use Illuminate\\Http\\Client\\PendingRequest;
use Illuminate\\Support\\Facades\\Http;
use Throwable;
$response = Http::retry(3, 100, function (Throwable $exception, PendingRequest $request) {
return $exception instanceof ConnectionException;
})->get('<https://api.example.com/status>');
リトライ後も失敗した場合、既定では ConnectionException や RequestException がスローされます。例外を出さず最後のレスポンスを返したいときは、throw: false を指定できます。
<?php
use Illuminate\\Support\\Facades\\Http;
$response = Http::retry(3, 100, throw: false)->get('<https://api.example.com/status>');
接続エラー時は、throw: false でも ConnectionException が残る点に注意してください。
サービスクラスに閉じ込める例
ルートやコントローラに Http:: を直書きすると、テストや再利用がしづらくなります。外部 API 呼び出しは app/Services/ 配下の専用クラスにまとめると扱いやすいです。
トークンは config/services.php と .env に置くのが一般的です。
// config/services.php
'example' => [
'token' => env('EXAMPLE_API_TOKEN'),
],
# .env
EXAMPLE_API_TOKEN=your-token-here
<?php
namespace App\\Services;
use Illuminate\\Http\\Client\\Response;
use Illuminate\\Support\\Facades\\Http;
class ExampleApiClient
{
public function fetchUser(int $id): Response
{
return Http::acceptJson()
->withToken(config('services.example.token'))
->timeout(5)
->retry(2, 200)
->get("<https://api.example.com/users/{$id}>")
->throw(); // 4xx/5xx 時は RequestException
}
}
Laravel はコンストラクタ引数がないサービスクラスを自動解決するため、app(ExampleApiClient::class) で取得できます。
テスト:Http::fake で外部通信を差し替える
Testing 節では、Http::fake でスタブレスポンスを返せます。PHPUnit / Pest どちらでも同じ API を使えます。
<?php
namespace Tests\\Feature;
use App\\Services\\ExampleApiClient;
use Illuminate\\Http\\Client\\Request;
use Illuminate\\Support\\Facades\\Http;
use Tests\\TestCase;
class ExampleApiClientTest extends TestCase
{
public function test_fetch_user_returns_stubbed_response(): void
{
Http::fake([
'api.example.com/users/*' => Http::response([
'id' => 1,
'name' => 'Taylor',
], 200),
]);
$response = app(ExampleApiClient::class)->fetchUser(1);
$this->assertSame('Taylor', $response->json('name'));
Http::assertSent(function (Request $request) {
return str_contains($request->url(), 'api.example.com/users/1')
&& $request->hasHeader('Authorization');
});
}
}
テスト中に意図しない本番 API へ通信しないよう、Http::preventStrayRequests() を基底クラスで有効にする方法もあります。fake されていない URL へのリクエストは例外になります。
<?php
namespace Tests;
use Illuminate\\Foundation\\Testing\\TestCase as BaseTestCase;
use Illuminate\\Support\\Facades\\Http;
abstract class TestCase extends BaseTestCase
{
protected function setUp(): void
{
parent::setUp();
Http::preventStrayRequests();
}
}
実装時の注意点
fake した URL パターンの一致
Http::fake のキーは URL の部分一致です。本番とテストで URL が異なると fake は効きません。その場合、実際の通信が走ります。preventStrayRequests と合わせて確認すると安全です。
retry の回数と相手 API への負荷
retry(3, 100) は最大 3 回試行を意味します。相手 API のレート制限がある場合、テストで Http::assertSentCount(3) を使い、失敗時に想定回数だけ送っているか確認するとよいです。
失敗例:500 レスポンスを成功と誤認した
外部 API 連携を初めて実装したとき、Http::post の戻り値をそのまま json() だけ読んで処理を進めていました。相手 API が一時的に 500 を返しても例外は発生せず、json() が null や空配列になり、後続処理で「データがない」扱いになったケースがあります。
原因は、Laravel が 4xx / 5xx で自動例外を投げない仕様を見落としていたことです。対処として、重要な呼び出しでは throw() または successful() による分岐を入れ、次のようにログにステータスコードとレスポンス本文を残すようにしました。
Log::warning('External API error', [
'status' => $response->status(),
'body' => $response->body(),
]);
学び:通信の「書き方」と「失敗の扱い」をセットで決める
Http Facade はリクエストを短く書ける入口です。同時に、失敗が静かに返る設計でもあるため、クライアントクラスごとに throw() か successful() をチームで決めると本番とテストの両方が安定しやすくなります。外部 API クライアントを 1 クラスにまとめ、Http::fake でそのクラスをテストする構成が、実務では扱いやすいことが多いです。
まとめ
HttpFacade は Guzzle ベースの HTTP クライアントで、GET / POST やヘッダー・認証をチェーンで指定できる。- 4xx / 5xx は既定では例外にならないため、
successful()やthrow()で明示的に扱う。 retryで一時的な失敗への再試行を設定でき、条件は callable で絞れる。- テストでは
Http::fakeとassertSentで外部通信なしに検証できる。
次に試せること
まだ Http を使っていない場合は、ルートや Tinker から 1 エンドポイントだけ Http::get で叩き、dd($response->status()) で動作確認するとよいです。
- 既存の外部 API 呼び出しを 1 箇所選び、
Http::timeoutとthrow()を追加してエラー時のログを確認する。 - その呼び出しをサービスクラスに移し、上記の
ExampleApiClientTestを参考に Feature テストを 1 本書く。 - 相手 API の SLA に合わせて
retryの回数と待機時間を調整し、テストでHttp::assertSentCountを使って想定回数だけ送っているか確認する。




