はじめに
業務アプリやバッチ処理のコードを読んでいると、こんな書き方を見かけることがあります。
foreach ($items as $item) {
$response = $externalService->fetch($item->id); // ← loop内でAPI叩き
...
}
一見すると普通ですが、実はこれ
パフォーマンス劣化・レートリミット・障害耐性低下
を同時に招く危険なアンチパターンです。
この記事では、
- なぜ loop 内で外部 API/Service を呼んではいけないのか
- 実際どんな問題が起きるのか
- どう直すべきか
- 良い例 / 悪い例をコード付きで解説
をまとめます。
結論
外部API・サービス呼び出しは 必ず loop の外に出すべき です。
☑️ 1. パフォーマンスが最悪になる(ネットワーク × N 回)
APIのレスポンスが 100ms の場合、100件処理しただけで 10秒 かかります。
内部処理は高速でも、ネットワークIOは常に最も遅い。
☑️ 2. レートリミットに簡単に到達する
外部APIはたいてい何らかの回数制限があります。
loop 内で呼ぶと、意図せず制限を踏み抜き、急にエラー多発 → 障害化。
☑️ 3. 外部障害の影響を全件が受けて止まる
loop 内で毎回 API を呼ぶと、
- APIが一時的に落ちる
- タイムアウトが発生する
だけで 全件処理が即停止 します。
外部依存処理は、障害時の影響範囲を最小化すべきです。
☑️ 4. 再実行が困難(どこまで進んだ?問題)
途中で落ちると「どこまで処理したか?」が不明になり、リトライがしにくい。
☑️ 5. 従量課金APIの場合はコストが爆増
ループ件数 × APIコスト = 請求額
開発者にとっては見えづらいが、運用コストを押し上げます。
❌ 悪い例:loop 内で API を叩く
foreach ($hotels as $hotel) {
$planId = $externalApi->getPlanId($hotel->plan_code); // ❌ 毎回API
$this->savePlan($hotel, $planId);
}
- 毎回API
- 障害が起きたら即終了
- 遅い
- コスト高い
典型的なアンチパターンです。
🟢 良い例:loop 外で一括取得する
☑️ API側がまとめ取得に対応している場合(最も理想)
$planCodes = $hotels->pluck('plan_code')->all();
// 一括で API 呼び出し(まとめて取得)
$planMap = $externalApi->getPlanIds($planCodes);
foreach ($hotels as $hotel) {
$planId = $planMap[$hotel->plan_code] ?? null;
$this->savePlan($hotel, $planId);
}
効果
- API コール回数 100 → 1 回
- パフォーマンス上昇
- 障害影響最小化
- コスト削減
「APIのあるべき姿」は bulk API を用意することです。
API側が対応していない場合:キャッシュで回避
APIがまとめ取得に対応していないケースでも、loop 内呼び出しを避ける工夫 ができます。
$cache = [];
foreach ($hotels as $hotel) {
// すでに取得したコードはAPI呼ばない
if (!isset($cache[$hotel->plan_code])) {
$cache[$hotel->plan_code] = $externalApi->getPlanId($hotel->plan_code);
}
$planId = $cache[$hotel->plan_code];
$this->savePlan($hotel, $planId);
}
重複コードに対する API 呼び出しが 1回に削減 されます。
🔧 どうしても loop 内で API が必要な場合の対策
① キャッシュする(最重要)
$cache = [];
foreach ($items as $item) {
if (!isset($cache[$item->code])) {
$cache[$item->code] = $api->fetch($item->code);
}
$value = $cache[$item->code];
}
② リトライ・サーキットブレーカーを設定する
障害中でも無限リトライしないようにする。
③ キューに逃がす
外部API依存処理はできるだけ非同期化。
④ まとめて投げられる API を作る(サーバ側改善)
自身でAPIを持っている場合はbulk API を用意するのが最強です。
「Service呼び出し」も同じ
外部APIだけでなく、以下も同じ原則が当てはまります。
- DBクエリ(N+1問題)
- 別マイクロサービス
- 外部ライブラリでのIO処理
- ファイルアクセス
loop 内の I/O は すべてアンチパターン になり得ます。
🎯 まとめ
外部APIやサービス呼び出しは、必ず loop の外に出すことを最優先で考えるべきです。
- 性能改善
- レートリミット回避
- 障害耐性アップ
- コスト削減
とメリットが非常に大きいです。
もし loop 内で呼ばれているコードを見つけたら、
「これ、外に出せない?」
と必ず疑って改善していきましょう。