SuperAgent

Node.jsとブラウザで動作する軽量で柔軟なHTTPクライアントライブラリ。チェーン可能なAPIにより直感的なリクエスト構築が可能。プラグインシステム、自動パース、マルチパート対応、プログレストラッキングなどの機能を提供。

HTTPクライアントJavaScriptNode.jsブラウザ軽量Promiseチェーン

GitHub概要

forwardemail/superagent

Ajax for Node.js and browsers (JS HTTP client). Maintained for @forwardemail, @ladjs, @spamscanner, @breejs, @cabinjs, and @lassjs.

スター16,624
ウォッチ222
フォーク1,329
作成日:2011年4月13日
言語:JavaScript
ライセンス:MIT License

トピックス

なし

スター履歴

forwardemail/superagent Star History
データ取得日時: 2025/7/18 06:03

ライブラリ

SuperAgent

概要

SuperAgentは「ブラウザとNode.js向けの軽量HTTPクライアント」として開発された、直感的で表現力豊かなAPIを提供するライブラリです。関数チェーンによる読みやすい構文、Promise/async-await対応、プラグインシステム、自動パース機能を内蔵し、フロントエンドとバックエンドの両方で利用可能。TJ Holowaychukによって作成され、Express.jsなどと同じエコシステムの一部として、JavaScript開発者に親しまれているHTTPクライアントライブラリです。

詳細

SuperAgent 2025年版は豊富な機能と使いやすさのバランスを保ちながら、モダンJavaScript開発に対応した成熟したHTTPクライアントとして評価されています。Express.jsとの親和性、プラグインによる拡張性、ブラウザ・Node.js統一API、タイムアウト・リトライ・進捗追跡機能により、多様な開発シーンで活用。JSON自動パース、フォームデータ処理、ファイルアップロード、レスポンス変換など豊富な機能を備え、学習コストと機能性のバランスが取れたライブラリとして位置づけられています。

主な特徴

  • 統一API: ブラウザとNode.js環境での一貫したインターフェース
  • チェーン可能API: 直感的で読みやすいメソッドチェーン構文
  • プラグインシステム: 機能拡張とカスタマイズの柔軟性
  • 自動パース: JSON、XML、フォームデータの自動解析
  • 進捗追跡: アップロード・ダウンロード進捗のリアルタイム監視
  • Express統合: Express.jsエコシステムとの優れた親和性

メリット・デメリット

メリット

  • 直感的なチェーン構文による読みやすいコード記述
  • ブラウザとNode.jsでの統一されたAPI体験
  • プラグインシステムによる豊富な拡張性
  • 自動JSON解析とレスポンス変換の便利機能
  • Express.jsなど同エコシステムとの優れた統合
  • 学習コストが低く初心者にも優しい設計

デメリット

  • Axios、Fetchなど最新ライブラリと比較して機能が限定的
  • TypeScript対応がコミュニティ依存で公式サポートが限定的
  • バンドルサイズがKyなど軽量ライブラリより大きい
  • HTTP/2対応などモダンHTTP機能のサポートが不十分
  • エラーハンドリングが他ライブラリより基本的
  • 長期的なメンテナンス性に不安あり

参考ページ

書き方の例

インストールと基本セットアップ

# SuperAgentのインストール
npm install superagent

# TypeScript型定義(コミュニティ版)
npm install @types/superagent --save-dev

# ESM環境での使用確認
node --version  # Node.js 14以降推奨
// ES6 Modules でのインポート
import superagent from 'superagent';

// CommonJS でのインポート
const superagent = require('superagent');

// ブラウザ環境での使用(CDN)
// <script src="https://unpkg.com/[email protected]/dist/superagent.min.js"></script>

基本的なリクエスト(GET/POST/PUT/DELETE)

import superagent from 'superagent';

// 基本的なGETリクエスト
async function basicGetRequest() {
  try {
    const response = await superagent
      .get('https://api.example.com/users')
      .set('Accept', 'application/json')
      .set('User-Agent', 'MyApp/1.0');
    
    console.log('ステータス:', response.status);
    console.log('ヘッダー:', response.headers);
    console.log('ユーザーデータ:', response.body); // 自動JSON解析
  } catch (error) {
    console.error('GETリクエストエラー:', error.message);
  }
}

// クエリパラメータ付きGETリクエスト
async function getWithQuery() {
  try {
    const response = await superagent
      .get('https://api.example.com/users')
      .query({
        page: 1,
        limit: 10,
        sort: 'created_at',
        filter: 'active'
      })
      .set('Authorization', 'Bearer your-jwt-token');
    
    console.log('検索結果:', response.body);
    console.log('リクエストURL:', response.request.url);
  } catch (error) {
    if (error.response) {
      console.error('HTTPエラー:', error.response.status, error.response.text);
    } else {
      console.error('ネットワークエラー:', error.message);
    }
  }
}

// POSTリクエスト(JSON送信)
async function postJsonRequest() {
  try {
    const userData = {
      name: '田中太郎',
      email: '[email protected]',
      age: 30
    };

    const response = await superagent
      .post('https://api.example.com/users')
      .send(userData) // 自動的にJSON形式で送信
      .set('Authorization', 'Bearer your-jwt-token')
      .set('Content-Type', 'application/json');
    
    console.log('作成されたユーザー:', response.body);
    console.log('作成日時:', response.headers['date']);
  } catch (error) {
    console.error('POSTエラー:', error.status, error.message);
  }
}

// PUTリクエスト(更新)
async function putRequest() {
  try {
    const updatedData = {
      name: '田中次郎',
      email: '[email protected]'
    };

    const response = await superagent
      .put('https://api.example.com/users/123')
      .send(updatedData)
      .set('Authorization', 'Bearer your-jwt-token');
    
    if (response.status === 200) {
      console.log('ユーザー更新成功:', response.body);
    }
  } catch (error) {
    console.error('更新エラー:', error.status, error.response?.text);
  }
}

// DELETEリクエスト
async function deleteRequest() {
  try {
    const response = await superagent
      .delete('https://api.example.com/users/123')
      .set('Authorization', 'Bearer your-jwt-token');
    
    if (response.status === 204) {
      console.log('ユーザー削除成功');
    }
  } catch (error) {
    console.error('削除エラー:', error.status);
  }
}

// フォームデータの送信
async function submitFormData() {
  try {
    const response = await superagent
      .post('https://api.example.com/login')
      .type('form') // application/x-www-form-urlencoded
      .send({
        username: 'testuser',
        password: 'secret123',
        remember: 'true'
      });
    
    console.log('ログイン成功:', response.body);
  } catch (error) {
    console.error('ログインエラー:', error.message);
  }
}

// レスポンス形式の詳細取得
async function detailedResponse() {
  try {
    const response = await superagent
      .get('https://api.example.com/status')
      .accept('json');
    
    console.log('ステータスコード:', response.status);
    console.log('ステータステキスト:', response.statusText);
    console.log('レスポンスタイプ:', response.type);
    console.log('文字エンコーディング:', response.charset);
    console.log('コンテンツ長:', response.headers['content-length']);
    console.log('レスポンス時間:', response.duration, 'ms');
  } catch (error) {
    console.error('リクエストエラー:', error);
  }
}

// 並列実行例
async function parallelRequests() {
  try {
    const [users, posts, comments] = await Promise.all([
      superagent.get('https://api.example.com/users'),
      superagent.get('https://api.example.com/posts'),
      superagent.get('https://api.example.com/comments')
    ]);

    console.log('ユーザー数:', users.body.length);
    console.log('投稿数:', posts.body.length);
    console.log('コメント数:', comments.body.length);
  } catch (error) {
    console.error('並列リクエストエラー:', error.message);
  }
}

高度な設定とカスタマイズ(ヘッダー、認証、タイムアウト等)

// カスタムヘッダーと認証設定
async function advancedHeaders() {
  try {
    const response = await superagent
      .get('https://api.example.com/protected')
      .set({
        'Authorization': 'Bearer your-jwt-token',
        'Accept': 'application/json',
        'User-Agent': 'MyApp/1.0 (SuperAgent)',
        'X-API-Version': 'v2',
        'X-Request-ID': generateRequestId(),
        'Accept-Language': 'ja-JP,en-US;q=0.9'
      })
      .timeout({
        response: 5000,  // レスポンス待機時間(5秒)
        deadline: 10000  // 全体の制限時間(10秒)
      });
    
    console.log('保護されたリソース:', response.body);
  } catch (error) {
    if (error.timeout) {
      console.error('タイムアウトエラー:', error.timeout);
    } else {
      console.error('認証エラー:', error.status);
    }
  }
}

// Basic認証
async function basicAuth() {
  try {
    const response = await superagent
      .get('https://api.example.com/basic-auth')
      .auth('username', 'password'); // Basic認証
    
    console.log('Basic認証成功:', response.body);
  } catch (error) {
    console.error('認証失敗:', error.status);
  }
}

// Bearer Token認証
async function bearerAuth() {
  try {
    const response = await superagent
      .get('https://api.example.com/bearer-auth')
      .auth('your-access-token', { type: 'bearer' });
    
    console.log('Bearer認証成功:', response.body);
  } catch (error) {
    console.error('Token認証失敗:', error.message);
  }
}

// プロキシ設定(Node.js環境)
async function proxyRequest() {
  try {
    const response = await superagent
      .get('https://api.example.com/data')
      .proxy('http://proxy.example.com:8080')
      .set('User-Agent', 'MyApp via Proxy');
    
    console.log('プロキシ経由レスポンス:', response.body);
  } catch (error) {
    console.error('プロキシエラー:', error.message);
  }
}

// SSL証明書の設定(Node.js環境)
async function sslConfiguration() {
  try {
    const fs = require('fs');
    
    const response = await superagent
      .get('https://secure-api.example.com/data')
      .ca(fs.readFileSync('/path/to/ca-certificate.pem'))
      .cert(fs.readFileSync('/path/to/client-certificate.pem'))
      .key(fs.readFileSync('/path/to/client-private-key.pem'))
      .passphrase('certificate-passphrase');
    
    console.log('SSL通信成功:', response.body);
  } catch (error) {
    console.error('SSL設定エラー:', error.message);
  }
}

// Cookieの処理
async function cookieHandling() {
  // Cookie送信
  try {
    const response = await superagent
      .get('https://api.example.com/session-data')
      .set('Cookie', 'session_id=abc123; user_pref=dark_mode');
    
    console.log('Cookieありレスポンス:', response.body);
    
    // レスポンスからCookieを取得
    const setCookieHeader = response.headers['set-cookie'];
    if (setCookieHeader) {
      console.log('受信Cookie:', setCookieHeader);
    }
  } catch (error) {
    console.error('Cookie処理エラー:', error.message);
  }
}

// リダイレクト制御
async function redirectControl() {
  try {
    // リダイレクトを無効化
    const response = await superagent
      .get('https://api.example.com/redirect-endpoint')
      .redirects(0); // リダイレクト無効
    
    console.log('リダイレクト無効レスポンス:', response.status);
  } catch (error) {
    if (error.status === 302) {
      console.log('リダイレクト検出:', error.response.headers.location);
    }
  }
  
  try {
    // 最大3回までリダイレクトを許可
    const response = await superagent
      .get('https://api.example.com/redirect-endpoint')
      .redirects(3);
    
    console.log('リダイレクト後のレスポンス:', response.body);
  } catch (error) {
    console.error('リダイレクトエラー:', error.message);
  }
}

// カスタムパーサー設定
async function customParser() {
  try {
    const response = await superagent
      .get('https://api.example.com/custom-data')
      .parse((res, callback) => {
        let data = '';
        res.on('data', chunk => {
          data += chunk;
        });
        res.on('end', () => {
          try {
            // カスタムパース処理
            const parsed = data.split('\n').map(line => line.trim());
            callback(null, parsed);
          } catch (err) {
            callback(err);
          }
        });
      });
    
    console.log('カスタムパース結果:', response.body);
  } catch (error) {
    console.error('パースエラー:', error.message);
  }
}

function generateRequestId() {
  return 'req-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
}

エラーハンドリングとリトライ機能

// 包括的なエラーハンドリング
async function comprehensiveErrorHandling() {
  try {
    const response = await superagent
      .get('https://api.example.com/users')
      .timeout({
        response: 5000,
        deadline: 10000
      })
      .retry(3); // 3回まで自動リトライ
    
    return response.body;
    
  } catch (error) {
    // SuperAgentエラーの詳細処理
    if (error.timeout) {
      console.error('タイムアウトエラー:');
      console.error('- レスポンス待機:', error.timeout.response ? '有' : '無');
      console.error('- 全体制限時間:', error.timeout.deadline ? '有' : '無');
    } else if (error.response) {
      // HTTPエラー(4xx, 5xx)
      const status = error.response.status;
      const errorBody = error.response.text;
      
      console.error('HTTPエラー:', status);
      
      switch (status) {
        case 400:
          console.error('Bad Request: リクエストパラメータを確認してください');
          break;
        case 401:
          console.error('Unauthorized: 認証情報が無効です');
          // トークンリフレッシュやログイン画面へリダイレクト
          break;
        case 403:
          console.error('Forbidden: アクセス権限がありません');
          break;
        case 404:
          console.error('Not Found: リソースが見つかりません');
          break;
        case 429:
          const retryAfter = error.response.headers['retry-after'];
          console.error('Rate Limited: 時間をおいて再試行してください');
          if (retryAfter) {
            console.error('再試行可能時間:', retryAfter + '秒後');
          }
          break;
        case 500:
          console.error('Internal Server Error: サーバー側の問題です');
          break;
        case 502:
          console.error('Bad Gateway: サーバーが一時的に利用不可です');
          break;
        case 503:
          console.error('Service Unavailable: サービスが過負荷状態です');
          break;
        default:
          console.error('エラー詳細:', errorBody);
      }
    } else {
      // ネットワークエラー
      console.error('ネットワークエラー:', error.message);
      console.error('接続先:', error.address);
      console.error('ポート:', error.port);
    }
    
    throw error;
  }
}

// カスタムリトライ戦略
async function customRetryStrategy() {
  const maxRetries = 3;
  const baseDelay = 1000; // 1秒
  
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      const response = await superagent
        .get('https://api.example.com/unstable-endpoint')
        .timeout({
          response: 5000,
          deadline: 10000
        });
      
      return response.body;
      
    } catch (error) {
      const isLastAttempt = attempt === maxRetries;
      
      if (isLastAttempt) {
        console.error('最大リトライ回数に達しました:', error.message);
        throw error;
      }
      
      // リトライ対象のエラーかチェック
      const shouldRetry = error.timeout || 
                         (error.response && [408, 429, 500, 502, 503, 504].includes(error.response.status));
      
      if (!shouldRetry) {
        console.error('リトライ対象外エラー:', error.message);
        throw error;
      }
      
      // 指数バックオフ
      const delay = baseDelay * Math.pow(2, attempt);
      console.log(`リトライ ${attempt + 1}/${maxRetries} - ${delay}ms後に再実行`);
      
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

// エラーステータス毎の個別処理
async function statusSpecificHandling() {
  try {
    const response = await superagent
      .get('https://api.example.com/conditional-endpoint')
      .ok(res => res.status < 500); // 500未満を成功として扱う
    
    return response.body;
    
  } catch (error) {
    if (error.response) {
      const status = error.response.status;
      
      if (status >= 400 && status < 500) {
        // クライアントエラー(4xx)
        console.log('クライアントエラーとして処理:', status);
        return { error: 'client_error', status, message: error.response.text };
      } else if (status >= 500) {
        // サーバーエラー(5xx)
        console.log('サーバーエラーとして処理:', status);
        throw error; // サーバーエラーは例外として扱う
      }
    }
    
    throw error;
  }
}

// 並列リクエストでの部分失敗許容
async function partialFailureHandling() {
  const urls = [
    'https://api.example.com/reliable',
    'https://api.example.com/unreliable', 
    'https://api.example.com/another'
  ];
  
  const requests = urls.map(url => 
    superagent.get(url)
      .timeout({ response: 3000 })
      .catch(error => ({
        error: true,
        url,
        message: error.message,
        status: error.response?.status
      }))
  );
  
  const results = await Promise.all(requests);
  
  const successful = results.filter(result => !result.error);
  const failed = results.filter(result => result.error);
  
  console.log('成功:', successful.length, '失敗:', failed.length);
  console.log('失敗詳細:', failed);
  
  return { successful, failed };
}

// プログレッシブな障害復旧
async function progressiveRecovery() {
  const endpoints = [
    'https://primary-api.example.com/data',
    'https://backup-api.example.com/data',
    'https://fallback-api.example.com/data'
  ];
  
  for (let i = 0; i < endpoints.length; i++) {
    try {
      console.log(`試行 ${i + 1}: ${endpoints[i]}`);
      
      const response = await superagent
        .get(endpoints[i])
        .timeout({ response: 3000 + (i * 2000) }) // 徐々にタイムアウトを延長
        .retry(i + 1); // 後のAPIほどリトライ回数を増加
      
      console.log(`エンドポイント ${i + 1} で成功`);
      return response.body;
      
    } catch (error) {
      console.log(`エンドポイント ${i + 1} 失敗:`, error.message);
      
      if (i === endpoints.length - 1) {
        console.error('全てのエンドポイントで失敗');
        throw new Error('All endpoints failed');
      }
    }
  }
}

並行処理と非同期リクエスト

// 複数リクエストの並列実行
async function parallelRequests() {
  try {
    const startTime = Date.now();
    
    const [users, posts, comments, categories] = await Promise.all([
      superagent.get('https://api.example.com/users'),
      superagent.get('https://api.example.com/posts'),
      superagent.get('https://api.example.com/comments'),
      superagent.get('https://api.example.com/categories')
    ]);
    
    const endTime = Date.now();
    console.log(`並列実行時間: ${endTime - startTime}ms`);
    
    return {
      users: users.body,
      posts: posts.body,
      comments: comments.body,
      categories: categories.body
    };
  } catch (error) {
    console.error('並列リクエストエラー:', error.message);
    throw error;
  }
}

// Promise.allSettledを使用した部分失敗許容
async function parallelWithPartialFailure() {
  const endpoints = [
    'https://api.example.com/endpoint1',
    'https://api.example.com/endpoint2',
    'https://api.example.com/endpoint3',
    'https://api.example.com/unreliable-endpoint'
  ];
  
  const requests = endpoints.map(url => superagent.get(url).timeout(5000));
  const results = await Promise.allSettled(requests);
  
  const successful = [];
  const failed = [];
  
  results.forEach((result, index) => {
    if (result.status === 'fulfilled') {
      successful.push({
        url: endpoints[index],
        data: result.value.body
      });
    } else {
      failed.push({
        url: endpoints[index],
        error: result.reason.message
      });
    }
  });
  
  console.log(`成功: ${successful.length}, 失敗: ${failed.length}`);
  return { successful, failed };
}

// ページネーション対応の順次データ取得
async function fetchAllPages() {
  const allData = [];
  let page = 1;
  let hasMore = true;
  
  while (hasMore) {
    try {
      const response = await superagent
        .get('https://api.example.com/paginated-data')
        .query({
          page: page,
          limit: 20
        })
        .timeout(10000);
      
      const pageData = response.body;
      allData.push(...pageData.items);
      
      hasMore = pageData.hasMore;
      page++;
      
      console.log(`ページ ${page - 1} 取得完了: ${pageData.items.length}件`);
      
      // API負荷軽減のための待機
      if (hasMore) {
        await new Promise(resolve => setTimeout(resolve, 200));
      }
      
    } catch (error) {
      console.error(`ページ ${page} 取得エラー:`, error.message);
      break;
    }
  }
  
  console.log(`合計 ${allData.length}件のデータを取得`);
  return allData;
}

// 並列ページネーション(高速データ取得)
async function parallelPagination() {
  // 最初のページで総ページ数を取得
  const firstPage = await superagent
    .get('https://api.example.com/paginated-data')
    .query({ page: 1, limit: 20 });
  
  const totalPages = firstPage.body.totalPages;
  const allData = [...firstPage.body.items];
  
  if (totalPages > 1) {
    // 残りのページを並列取得
    const pageNumbers = Array.from({ length: totalPages - 1 }, (_, i) => i + 2);
    const pageRequests = pageNumbers.map(page =>
      superagent
        .get('https://api.example.com/paginated-data')
        .query({ page, limit: 20 })
        .timeout(10000)
    );
    
    const pageResults = await Promise.allSettled(pageRequests);
    
    pageResults.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        allData.push(...result.value.body.items);
      } else {
        console.error(`ページ ${pageNumbers[index]} 失敗:`, result.reason.message);
      }
    });
  }
  
  console.log(`並列取得完了: ${allData.length}件`);
  return allData;
}

// レート制限対応の順次実行
async function rateLimitedRequests(urls, requestsPerSecond = 5) {
  const interval = 1000 / requestsPerSecond; // リクエスト間隔
  const results = [];
  
  for (let i = 0; i < urls.length; i++) {
    const startTime = Date.now();
    
    try {
      const response = await superagent
        .get(urls[i])
        .timeout(5000);
      
      results.push({
        url: urls[i],
        success: true,
        data: response.body
      });
      
      console.log(`${i + 1}/${urls.length} 完了: ${urls[i]}`);
      
    } catch (error) {
      results.push({
        url: urls[i],
        success: false,
        error: error.message
      });
      
      console.error(`${i + 1}/${urls.length} 失敗: ${urls[i]}`);
    }
    
    // レート制限対応の待機
    if (i < urls.length - 1) {
      const elapsed = Date.now() - startTime;
      const waitTime = Math.max(0, interval - elapsed);
      
      if (waitTime > 0) {
        await new Promise(resolve => setTimeout(resolve, waitTime));
      }
    }
  }
  
  const successCount = results.filter(r => r.success).length;
  console.log(`処理完了: ${successCount}/${urls.length} 成功`);
  
  return results;
}

// 同時実行数制限付き並列処理
async function concurrencyLimitedRequests(urls, maxConcurrency = 3) {
  const results = [];
  const executing = [];
  
  for (const url of urls) {
    const promise = superagent.get(url).timeout(5000).then(
      response => ({ url, success: true, data: response.body }),
      error => ({ url, success: false, error: error.message })
    );
    
    results.push(promise);
    
    if (urls.length >= maxConcurrency) {
      executing.push(promise);
      
      if (executing.length >= maxConcurrency) {
        await Promise.race(executing);
        executing.splice(executing.findIndex(p => p === promise), 1);
      }
    }
  }
  
  const finalResults = await Promise.all(results);
  const successCount = finalResults.filter(r => r.success).length;
  
  console.log(`同時実行制限処理完了: ${successCount}/${urls.length} 成功`);
  return finalResults;
}

フレームワーク統合と実用例

// Express.js統合例
import express from 'express';
import superagent from 'superagent';

const app = express();
app.use(express.json());

// APIプロキシエンドポイント
app.get('/api/proxy/:service/*', async (req, res) => {
  try {
    const { service } = req.params;
    const path = req.params[0];
    const serviceUrls = {
      'users': 'https://users-api.example.com',
      'posts': 'https://posts-api.example.com',
      'comments': 'https://comments-api.example.com'
    };
    
    const baseUrl = serviceUrls[service];
    if (!baseUrl) {
      return res.status(404).json({ error: 'Service not found' });
    }
    
    const response = await superagent
      .get(`${baseUrl}/${path}`)
      .query(req.query)
      .set('Authorization', req.headers.authorization)
      .timeout(10000);
    
    res.json(response.body);
  } catch (error) {
    console.error('プロキシエラー:', error.message);
    res.status(error.response?.status || 500).json({
      error: 'Proxy request failed',
      message: error.message
    });
  }
});

// React/Vue.js統合例(APIクライアントサービス)
class ApiService {
  constructor(baseURL, token) {
    this.baseURL = baseURL;
    this.token = token;
  }
  
  // 共通リクエスト設定
  request() {
    return superagent
      .timeout({
        response: 10000,
        deadline: 15000
      })
      .set('Authorization', `Bearer ${this.token}`)
      .set('Content-Type', 'application/json');
  }
  
  // ユーザー一覧取得
  async getUsers(page = 1, limit = 20) {
    try {
      const response = await this.request()
        .get(`${this.baseURL}/users`)
        .query({ page, limit });
      
      return {
        data: response.body,
        pagination: {
          page,
          limit,
          total: parseInt(response.headers['x-total-count'])
        }
      };
    } catch (error) {
      throw this.handleError(error);
    }
  }
  
  // ユーザー作成
  async createUser(userData) {
    try {
      const response = await this.request()
        .post(`${this.baseURL}/users`)
        .send(userData);
      
      return response.body;
    } catch (error) {
      throw this.handleError(error);
    }
  }
  
  // ユーザー更新
  async updateUser(id, userData) {
    try {
      const response = await this.request()
        .put(`${this.baseURL}/users/${id}`)
        .send(userData);
      
      return response.body;
    } catch (error) {
      throw this.handleError(error);
    }
  }
  
  // ユーザー削除
  async deleteUser(id) {
    try {
      await this.request()
        .delete(`${this.baseURL}/users/${id}`);
      
      return true;
    } catch (error) {
      throw this.handleError(error);
    }
  }
  
  // エラーハンドリング
  handleError(error) {
    if (error.response) {
      return {
        status: error.response.status,
        message: error.response.text || error.message,
        data: error.response.body
      };
    } else {
      return {
        status: 0,
        message: error.message,
        data: null
      };
    }
  }
}

// ファイルアップロード機能
async function uploadFile(file, metadata = {}) {
  try {
    const response = await superagent
      .post('https://api.example.com/upload')
      .attach('file', file, file.name)
      .field('description', metadata.description || '')
      .field('category', metadata.category || 'general')
      .set('Authorization', 'Bearer your-token')
      .on('progress', (event) => {
        if (event.direction === 'upload') {
          const percent = Math.round((event.loaded / event.total) * 100);
          console.log(`アップロード進捗: ${percent}%`);
        }
      });
    
    console.log('アップロード完了:', response.body);
    return response.body;
    
  } catch (error) {
    console.error('アップロードエラー:', error.message);
    throw error;
  }
}

// ダウンロード進捗追跡
async function downloadFile(url, filename) {
  try {
    const response = await superagent
      .get(url)
      .on('progress', (event) => {
        if (event.direction === 'download') {
          const percent = Math.round((event.loaded / event.total) * 100);
          console.log(`ダウンロード進捗: ${percent}%`);
        }
      })
      .buffer(true)
      .parse(superagent.parse.image); // 画像として解析
    
    // Node.js環境でのファイル保存
    if (typeof window === 'undefined') {
      const fs = require('fs');
      fs.writeFileSync(filename, response.body);
      console.log('ファイル保存完了:', filename);
    } else {
      // ブラウザ環境でのダウンロード
      const blob = new Blob([response.body]);
      const downloadUrl = window.URL.createObjectURL(blob);
      const link = document.createElement('a');
      link.href = downloadUrl;
      link.download = filename;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
      window.URL.revokeObjectURL(downloadUrl);
    }
    
  } catch (error) {
    console.error('ダウンロードエラー:', error.message);
    throw error;
  }
}

// プラグインシステムの活用
function createAuthPlugin(getToken) {
  return function(request) {
    const token = getToken();
    if (token) {
      request.set('Authorization', `Bearer ${token}`);
    }
    return request;
  };
}

function createLoggingPlugin() {
  return function(request) {
    const startTime = Date.now();
    const method = request.method;
    const url = request.url;
    
    console.log(`[${method}] ${url} - 開始`);
    
    request.on('response', (response) => {
      const duration = Date.now() - startTime;
      console.log(`[${method}] ${url} - 完了 (${response.status}, ${duration}ms)`);
    });
    
    request.on('error', (error) => {
      const duration = Date.now() - startTime;
      console.log(`[${method}] ${url} - エラー (${duration}ms):`, error.message);
    });
    
    return request;
  };
}

// プラグイン使用例
const authPlugin = createAuthPlugin(() => localStorage.getItem('token'));
const loggingPlugin = createLoggingPlugin();

async function apiCallWithPlugins() {
  try {
    const response = await superagent
      .get('https://api.example.com/protected')
      .use(authPlugin)
      .use(loggingPlugin);
    
    return response.body;
  } catch (error) {
    console.error('API呼び出しエラー:', error.message);
    throw error;
  }
}

// Webソケット風のポーリング実装
class ApiPoller {
  constructor(url, interval = 5000) {
    this.url = url;
    this.interval = interval;
    this.isPolling = false;
    this.callbacks = [];
  }
  
  onData(callback) {
    this.callbacks.push(callback);
  }
  
  async start() {
    this.isPolling = true;
    
    while (this.isPolling) {
      try {
        const response = await superagent
          .get(this.url)
          .timeout(this.interval - 1000);
        
        this.callbacks.forEach(callback => callback(response.body));
        
      } catch (error) {
        console.error('ポーリングエラー:', error.message);
      }
      
      if (this.isPolling) {
        await new Promise(resolve => setTimeout(resolve, this.interval));
      }
    }
  }
  
  stop() {
    this.isPolling = false;
  }
}

// 使用例
const poller = new ApiPoller('https://api.example.com/live-data');
poller.onData(data => console.log('新しいデータ:', data));
// poller.start();