リアルタイムAPIに最適?WebSocket vs Server-Sent Events 比較

Ashley Innocent

Ashley Innocent

13 3月 2026

リアルタイムAPIに最適?WebSocket vs Server-Sent Events 比較

Apidog エンタープライズ

オンプレミスデプロイ

SSO & RBAC

SOC 2 準拠

Apidog Enterpriseを見る

要約 (TL;DR)

通知やライブフィードのような一方向のサーバーからクライアントへの更新には、Server-Sent Events (SSE) を使用します。チャットやゲームのような双方向通信には WebSocket を使用します。SSE はよりシンプルで HTTP 上で動作します。WebSocket はより複雑ですが、双方向メッセージングをサポートします。Modern PetstoreAPI は、異なるリアルタイムのユースケースのために両方を実装しています。

はじめに

API でリアルタイムの更新が必要だとします。ペットのステータスが「利用可能」から「引き取られました」に変わったとき、クライアントはすぐに知る必要があります。このとき、WebSocket と Server-Sent Events (SSE) のどちらを使うべきでしょうか?

ほとんどの開発者は「より強力な」WebSocket をデフォルトで選びがちです。しかし、SSE の方が多くの場合、より良い選択肢です。SSE はよりシンプルで、標準の HTTP 上で動作し、自動再接続を処理します。WebSocket は、必要ないかもしれない複雑さを追加します。

Modern PetstoreAPI は両方のプロトコルを実装しています。ペットのステータス更新や注文通知には SSE を、ライブオークションの入札やリアルタイムチャットには WebSocket を使用しています。それぞれのプロトコルが異なるユースケースに対応しています。

💡
リアルタイム API を構築またはテストしている場合、Apidog は SSE と WebSocket の両方のテストをサポートしています。イベントストリームのテスト、メッセージ形式の検証、再接続シナリオのシミュレーションが可能です。
button

このガイドでは、SSE と WebSocket の違いを学び、Modern PetstoreAPI の実際の例を見て、それぞれのプロトコルをいつ使用すべきかを発見します。

Server-Sent Events (SSE) とは?

SSE は、サーバーからクライアントへイベントをストリーミングするための HTTP ベースのプロトコルです。

SSE の仕組み

クライアントは接続を開き、発生したイベントを受け取ります。

const eventSource = new EventSource('https://petstoreapi.com/v1/pets/notifications');

eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Pet update:', data);
};

eventSource.addEventListener('adoption', (event) => {
  const data = JSON.parse(event.data);
  console.log('Pet adopted:', data.petId);
});

サーバーはイベントを送信します。

GET /v1/pets/notifications
Accept: text/event-stream

HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache

event: adoption
data: {"petId":"019b4132","userId":"user-456"}

event: status-change
data: {"petId":"019b4127","status":"AVAILABLE"}

SSE の特徴

1. 一方向通信

サーバーがクライアントにプッシュします。クライアントは SSE 接続を通じてメッセージを送信することはできません(ただし、通常の HTTP リクエストを使用することはできます)。

2. HTTP 上に構築

標準 HTTP を使用します。プロキシ、ファイアウォール、CDN を経由して動作します。

3. 自動再接続

接続が切断された場合、ブラウザは自動的に再接続します。

4. 再開のためのイベント ID

サーバーはイベント ID を送信できます。クライアントは最後に受信したイベントから再開します。

id: 123
event: adoption
data: {"petId":"019b4132"}

id: 124
event: status-change
data: {"petId":"019b4127"}

切断された場合、クライアントは Last-Event-ID: 124 ヘッダーを送信して再開します。

5. シンプルなプロトコル

テキストベースの形式。curl で簡単にデバッグできます。

curl -N -H "Accept: text/event-stream" \
  https://petstoreapi.com/v1/pets/notifications

SSE の制限

1. 一方向のみ

クライアントは SSE 経由でメッセージを送信できません。クライアントからサーバーへの通信には別の HTTP リクエストが必要です。

2. テキストのみ

SSE はテキストを送信します。バイナリデータは Base64 エンコードする必要があります。

3. ブラウザの接続制限

ブラウザはドメインごとの SSE 接続を制限しています(通常 6 個)。ほとんどのアプリでは問題になりません。

4. 組み込みの圧縮機能なし

HTTP 圧縮は機能しますが、WebSocket のようなプロトコルレベルの圧縮はありません。

WebSocket とは?

WebSocket は、永続的な接続を介した全二重双方向プロトコルです。

WebSocket の仕組み

クライアントとサーバーはいつでもメッセージを送受信できます。

const ws = new WebSocket('wss://petstoreapi.com/auctions/019b4132');

// サーバーへメッセージを送信
ws.send(JSON.stringify({
  type: 'bid',
  amount: 500
}));

// サーバーからメッセージを受信
ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Auction update:', data);
};

ws.onclose = () => {
  console.log('Connection closed');
  // 手動での再接続ロジックが必要
};

サーバーはいつでも送信できます。

{"type":"bid","userId":"user-456","amount":550}
{"type":"outbid","newAmount":550}

クライアントはいつでも送信できます。

{"type":"bid","amount":600}
{"type":"watch","petId":"019b4132"}

WebSocket の特徴

1. 双方向

クライアントとサーバーの両方がいつでもメッセージを送信できます。真の双方向通信です。

2. 低遅延

永続的な接続。メッセージごとの HTTP オーバーヘッドはありません。ゲーム、チャット、ライブコラボレーションに最適です。

3. バイナリサポート

バイナリデータを直接送信できます。Base64 エンコードは不要です。

4. カスタムプロトコル

ws:// または wss://(セキュア)を使用します。初期ハンドシェイク後は HTTP ではありません。

5. フレームベース

メッセージはフレーム化されます。部分的なメッセージを送信し、再構成できます。

WebSocket の制限

1. 複雑なセットアップ

WebSocket サーバーが必要です。HTTP エンドポイントよりも複雑です。

2. 手動再接続

自動再接続機能はありません。リトライロジックを自分で実装する必要があります。

3. プロキシの問題

一部の企業プロキシは WebSocket をブロックします。HTTP プロキシは ws:// を理解しません。

4. ステートフル

サーバーは接続を追跡する必要があります。ステートレスな HTTP よりもスケーリングが困難です。

5. HTTP 機能なし

ハンドシェイク後は HTTP キャッシュ、ステータスコード、標準ヘッダーを使用できません。

比較表

機能 SSE WebSocket
方向性 サーバー → クライアント 双方向
プロトコル HTTP カスタム (ws://)
再接続 自動 手動
ブラウザサポート すべてのモダンブラウザ すべてのモダンブラウザ
プロキシフレンドリー はい 時々
複雑さ シンプル 複雑
バイナリデータ なし (テキストのみ) あり
遅延 低い 非常に低い
スケーラビリティ 高い (ステートレス) 中程度 (ステートフル)
ユースケース 通知、フィード チャット、ゲーム、コラボレーション

Modern PetstoreAPI が両方をどのように使用しているか

Modern PetstoreAPI は、異なるシナリオのために SSE と WebSocket の両方を実装しています。

ペットの更新のための SSE

エンドポイント: GET /v1/pets/notifications

const events = new EventSource(
  'https://petstoreapi.com/v1/pets/notifications?userId=user-456'
);

events.addEventListener('adoption', (e) => {
  const data = JSON.parse(e.data);
  showNotification(`${data.petName} was adopted!`);
});

events.addEventListener('status-change', (e) => {
  const data = JSON.parse(e.data);
  updatePetStatus(data.petId, data.status);
});

サーバー実装:

app.get('/v1/pets/notifications', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');

  const userId = req.query.userId;

  // ペットの更新を購読
  const subscription = petUpdates.subscribe(userId, (event) => {
    res.write(`event: ${event.type}\n`);
    res.write(`data: ${JSON.stringify(event.data)}\n\n`);
  });

  req.on('close', () => {
    subscription.unsubscribe();
  });
});

ユースケース:

ライブオークションのための WebSocket

エンドポイント: wss://petstoreapi.com/auctions/{auctionId}

const ws = new WebSocket('wss://petstoreapi.com/auctions/019b4132');

// 入札
function placeBid(amount) {
  ws.send(JSON.stringify({
    type: 'bid',
    amount
  }));
}

// 更新の受信
ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  switch (msg.type) {
    case 'bid':
      updateCurrentBid(msg.amount, msg.userId);
      break;
    case 'outbid':
      showOutbidNotification(msg.newAmount);
      break;
    case 'auction-end':
      showAuctionResult(msg.winner);
      break;
  }
};

サーバー実装:

wss.on('connection', (ws, req) => {
  const auctionId = req.params.auctionId;
  const auction = auctions.get(auctionId);

  ws.on('message', (data) => {
    const msg = JSON.parse(data);

    if (msg.type === 'bid') {
      const result = auction.placeBid(msg.userId, msg.amount);

      // 全参加者にブロードキャスト
      auction.participants.forEach(participant => {
        participant.send(JSON.stringify({
          type: 'bid',
          userId: msg.userId,
          amount: msg.amount
        }));
      });
    }
  });
});

ユースケース:

Apidog を使用したリアルタイム API のテスト

Apidog は、SSE と WebSocket API の両方のテストをサポートしています。

SSE のテスト

1. SSE リクエストを作成:

GET https://petstoreapi.com/v1/pets/notifications
Accept: text/event-stream

2. イベントを検証:

3. テストシナリオ:

WebSocket のテスト

1. WebSocket 接続を作成:

wss://petstoreapi.com/auctions/019b4132

2. テストメッセージを送信:

{"type":"bid","amount":500}
{"type":"watch","petId":"019b4132"}

3. レスポンスを検証:

4. テストシナリオ:

それぞれの使い分け

SSE を使用する場合:

例:

WebSocket を使用する場合:

例:

次の理由だけで WebSocket を使用しないこと:

❌ 「より高度だから」 - メリットのない複雑さ

❌ 「みんな使っているから」 - SSE の方がシンプルな場合が多い

❌ 「速いから」 - ほとんどのユースケースでは SSE で十分な速さ

❌ 「双方向だから」 - 実際に双方向性が必要ですか?

結論

SSE と WebSocket は両方ともリアルタイム通信を可能にしますが、それぞれ異なるシナリオのために設計されています。SSE は、自動再接続と HTTP 互換性を備えた一方向のサーバーからクライアントへの更新に優れています。WebSocket は、双方向で低遅延の通信で輝きます。

Modern PetstoreAPI は、両方のプロトコルを効果的に使用する方法を示しています。通知やステータス更新には SSE を、ライブオークションやチャットには WebSocket を使用します。どちらのプロトコルが「より優れている」ように見えるかではなく、ユースケースに基づいて選択してください。

さまざまなシナリオで SSE と WebSocket の両方の実装が正しく機能することを確認するために、Apidog を使用してリアルタイム API をテストしてください。

よくある質問

SSE は企業ファイアウォールを通過できますか?

はい。SSE は標準 HTTP を使用するため、HTTP プロキシやファイアウォールを通過して動作します。WebSocket はカスタムプロトコルを使用するため、一部のプロキシではブロックされる可能性があります。

WebSocket は SSE よりも高速ですか?

WebSocket はわずかに低い遅延(メッセージごとの HTTP オーバーヘッドがないため)を持ちますが、ほとんどのアプリケーションではその差はごくわずかです。SSE は通知、フィード、ステータス更新には十分な速さです。

SSE の再接続はどのように処理しますか?

ブラウザが自動的に再接続を処理します。サーバーからイベント ID を送信すると、クライアントは Last-Event-ID ヘッダーを使用して最後に受信したイベントから再開します。

モバイルアプリで SSE を使用できますか?

はい。iOS および Android は、ネイティブの HTTP クライアントまたはライブラリを介して SSE をサポートしています。SSE は HTTP が動作する場所ならどこでも動作します。

SSE 接続の最大接続時間はどれくらいですか?

厳密な制限はありません。SSE 接続は無期限に開いたままにできます。一部のプロキシやロードバランサーはタイムアウト(通常 30~60 秒)を設定している場合がありますが、ブラウザは自動的に再接続します。

WebSocket はバイナリデータを送信できますか?

はい。WebSocket はテキストフレームとバイナリフレームの両方をサポートしています。画像、音声、またはあらゆるバイナリデータを Base64 エンコードなしで送信できます。

ブラウザはいくつの SSE 接続を持てますか?

ブラウザはドメインごとの SSE 接続を制限しています(通常 6 個)。これはめったに問題になりません。ほとんどのアプリでは 1~2 個の SSE 接続しか必要ありません。

SSE のために特別なサーバーが必要ですか?

いいえ。どの HTTP サーバーでも SSE を処理できます。正しいヘッダー(Content-Type: text/event-stream)を設定し、接続を開いたままにするだけです。

ApidogでAPIデザイン中心のアプローチを取る

APIの開発と利用をよりシンプルなことにする方法を発見できる