Got

Node.js向けの高性能HTTPクライアントライブラリ。Axiosと同等の豊富な機能を提供しながら、より優れたパフォーマンスを実現。ストリーム対応、高度なエラーハンドリング、自動リトライ、キャッシュ機能、プロミス・ストリーム・コールバック全APIをサポート。

HTTPクライアントNode.jsJavaScript高性能Promiseストリーム

GitHub概要

sindresorhus/got

🌐 Human-friendly and powerful HTTP request library for Node.js

スター14,672
ウォッチ111
フォーク966
作成日:2014年3月27日
言語:TypeScript
ライセンス:MIT License

トピックス

httphttp-clienthttp-requesthttpsnodejsnpm-package

スター履歴

sindresorhus/got Star History
データ取得日時: 2025/7/18 06:02

ライブラリ

Got

概要

Gotは「Node.js向けの高性能HTTPクライアントライブラリ」として開発され、Axiosと同等の豊富な機能を提供しながら、より優れたパフォーマンスを実現します。ストリーム対応、高度なエラーハンドリング、自動リトライ、キャッシュ機能、プロミス・ストリーム・コールバック全APIをサポート。シンドレ・ソルハス(Sindre Sorhus)氏による開発で、Node.js環境における高性能HTTPクライアントとして確固たる地位を築いています。

詳細

Got 2025年版はNode.js環境における高性能HTTPクライアントの有力候補として地位を確立しています。特にAPIサーバーやマイクロサービス開発でパフォーマンス重視の選択として採用が増加し、豊富な機能と高速性の組み合わせで注目を集めています。Promise-based設計に加えて、Node.jsストリームネイティブサポート、高度なリトライ機構、カスタマイズ可能なフック システム、包括的なエラーハンドリングを提供。HTTP/2対応、コネクションプーリング、自動リダイレクト処理により、エンタープライズレベルのHTTP通信を実現します。

主な特徴

  • 高性能アーキテクチャ: Node.js最適化による高速HTTP通信処理
  • ストリーミング対応: ネイティブNode.jsストリームサポート
  • 高度なリトライ機構: カスタマイズ可能な指数バックオフリトライ
  • 包括的エラーハンドリング: 詳細なエラー情報とコンテキスト提供
  • フックシステム: リクエスト/レスポンスライフサイクル制御
  • キャッシュ機能: 組み込みレスポンスキャッシング機能

メリット・デメリット

メリット

  • Node.js環境でAxiosを上回る高いパフォーマンスと処理速度
  • ストリーミング対応による大容量データの効率的な処理
  • 高度なリトライ機構とエラーハンドリングによる堅牢性
  • HTTP/2サポートとコネクションプーリングによる通信効率向上
  • 豊富なカスタマイズオプションと柔軟なAPI設計
  • TypeScript完全サポートによる型安全な開発

デメリット

  • Node.js専用でブラウザ環境では利用不可
  • 学習コストがAxiosより高く、高度な機能の理解が必要
  • ブラウザとの互換性がないためユニバーサルアプリケーションに不適
  • 設定オプションが多く初期設定が複雑になる場合がある
  • エコシステムがAxiosほど成熟していない
  • ドキュメントや学習リソースがAxiosより限定的

参考ページ

書き方の例

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

# Gotのインストール
npm install got

# TypeScript型定義は自動的に含まれる
# @types/gotは不要

# ESMプロジェクトでの使用推奨
# CommonJSでは特別な設定が必要

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

// ES6 modulesでのインポート(推奨)
import got from 'got';

// 基本的なGETリクエスト
try {
  const { body } = await got('https://api.example.com/users');
  console.log('レスポンス:', body);
} catch (error) {
  console.error('エラー:', error);
}

// JSONレスポンスの取得
const data = await got.get('https://api.example.com/users').json();
console.log('ユーザーデータ:', data);

// TypeScript型指定付きリクエスト
interface User {
  id: number;
  name: string;
  email: string;
}

const users = await got.get('https://api.example.com/users').json<User[]>();
console.log('型安全なユーザーデータ:', users);

// POSTリクエスト(JSON送信)
const newUser = await got.post('https://api.example.com/users', {
  json: {
    name: '田中太郎',
    email: '[email protected]'
  }
}).json();

console.log('作成されたユーザー:', newUser);

// PUTリクエスト(更新)
const updatedUser = await got.put('https://api.example.com/users/123', {
  json: {
    name: '田中次郎',
    email: '[email protected]'
  }
}).json();

// DELETEリクエスト
await got.delete('https://api.example.com/users/123');
console.log('ユーザーを削除しました');

// 異なるレスポンス形式の取得
const textResponse = await got.get('https://api.example.com/message').text();
const bufferResponse = await got.get('https://api.example.com/binary').buffer();

// ヘッダー情報の取得
const response = await got.get('https://api.example.com/info');
console.log('ステータス:', response.statusCode);
console.log('ヘッダー:', response.headers);
console.log('URL:', response.url);

// searchParamsによるクエリパラメータ指定
const searchData = await got.get('https://api.example.com/search', {
  searchParams: {
    q: 'Node.js',
    page: 1,
    limit: 10
  }
}).json();

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

// カスタムヘッダーと認証
const authenticatedData = await got.get('https://api.example.com/private', {
  headers: {
    'Authorization': 'Bearer your-jwt-token',
    'Accept': 'application/json',
    'User-Agent': 'MyApp/1.0'
  }
}).json();

// タイムアウト設定
const dataWithTimeout = await got.get('https://api.example.com/slow', {
  timeout: {
    request: 5000 // 5秒でタイムアウト
  }
}).json();

// 詳細なタイムアウト設定
const detailedTimeout = await got.get('https://api.example.com/data', {
  timeout: {
    lookup: 100,     // DNS解決タイムアウト
    connect: 1000,   // 接続タイムアウト
    secureConnect: 1000, // TLS接続タイムアウト
    socket: 2000,    // ソケットアイドルタイムアウト
    send: 5000,      // 送信タイムアウト
    response: 10000, // レスポンスタイムアウト
    request: 30000   // リクエスト全体タイムアウト
  }
}).json();

// プレフィックスURLを使用したクライアント作成
const apiClient = got.extend({
  prefixUrl: 'https://api.example.com/v1',
  headers: {
    'User-Agent': 'MyApp/1.0'
  },
  timeout: {
    request: 10000
  }
});

// ベースクライアントを使用したリクエスト
const userData = await apiClient.get('users/123').json();
const newPost = await apiClient.post('posts', {
  json: { title: 'タイトル', content: 'コンテンツ' }
}).json();

// HTTPS設定とクライアント証明書
const secureData = await got.get('https://secure-api.example.com/data', {
  https: {
    rejectUnauthorized: true,
    cert: fs.readFileSync('./client.crt'),
    key: fs.readFileSync('./client.key'),
    ca: fs.readFileSync('./ca.crt')
  }
}).json();

// プロキシ設定
const proxyData = await got.get('https://api.example.com/data', {
  agent: {
    http: new HttpProxyAgent('http://proxy.example.com:8080'),
    https: new HttpsProxyAgent('https://proxy.example.com:8080')
  }
}).json();

// Cookie設定
const cookieJar = new CookieJar();
const dataWithCookies = await got.get('https://api.example.com/data', {
  cookieJar: cookieJar
}).json();

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

// 包括的なエラーハンドリング
try {
  const data = await got.get('https://api.example.com/users').json();
} catch (error) {
  console.log('エラー名:', error.name);
  console.log('エラーメッセージ:', error.message);
  
  if (error.response) {
    console.log('ステータスコード:', error.response.statusCode);
    console.log('レスポンスボディ:', error.response.body);
    console.log('リクエストURL:', error.response.url);
    console.log('レスポンスヘッダー:', error.response.headers);
  }
  
  // エラータイプ別処理
  if (error.name === 'HTTPError') {
    console.log('HTTP エラー:', error.response.statusCode);
  } else if (error.name === 'TimeoutError') {
    console.log('タイムアウトエラー');
  } else if (error.name === 'RequestError') {
    console.log('ネットワークエラー');
  } else if (error.name === 'ParseError') {
    console.log('レスポンス解析エラー');
  }
}

// カスタムリトライ設定
const dataWithRetry = await got.get('https://api.example.com/unstable', {
  retry: {
    limit: 5, // 最大5回リトライ
    methods: ['GET', 'POST', 'PUT'], // リトライ対象メソッド
    statusCodes: [408, 413, 429, 500, 502, 503, 504], // リトライ対象ステータス
    errorCodes: ['ETIMEDOUT', 'ECONNRESET', 'ECONNREFUSED'], // リトライ対象エラー
    calculateDelay: ({ computedValue }) => computedValue / 2 // 遅延時間カスタマイズ
  }
}).json();

// beforeRetryフックでリトライ情報ログ出力
const dataWithRetryLog = await got.get('https://api.example.com/flaky', {
  retry: {
    limit: 3
  },
  hooks: {
    beforeRetry: [
      (error, retryCount) => {
        console.log(`リトライ [${retryCount}]: ${error.code || error.message}`);
      }
    ]
  }
}).json();

// beforeErrorフックでエラーカスタマイズ
const dataWithCustomError = await got.get('https://api.example.com/data', {
  hooks: {
    beforeError: [
      error => {
        const { response } = error;
        if (response && response.body) {
          error.name = 'CustomAPIError';
          error.message = `APIエラー: ${response.body.message || response.statusCode}`;
        }
        return error;
      }
    ]
  }
}).json();

// リトライの無効化
const noRetryClient = got.extend({
  retry: {
    limit: 0
  }
});

// throwHttpErrorsを無効にしてエラーレスポンスを取得
const response = await got.get('https://api.example.com/maybe-error', {
  throwHttpErrors: false
});

if (response.statusCode >= 400) {
  console.log('エラーレスポンス:', response.statusCode);
  console.log('エラー内容:', response.body);
} else {
  console.log('成功レスポンス:', response.body);
}

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

// 複数リクエストの並列実行
async function fetchMultipleEndpoints() {
  try {
    const [users, posts, comments] = await Promise.all([
      got.get('https://api.example.com/users').json(),
      got.get('https://api.example.com/posts').json(),
      got.get('https://api.example.com/comments').json()
    ]);

    console.log('ユーザー:', users);
    console.log('投稿:', posts);
    console.log('コメント:', comments);

    return { users, posts, comments };
  } catch (error) {
    console.error('並列リクエストエラー:', error);
    throw error;
  }
}

// Promise.allSettledによる部分失敗許容
async function fetchWithPartialFailure() {
  const requests = [
    got.get('https://api.example.com/reliable').json(),
    got.get('https://api.example.com/unreliable').json(),
    got.get('https://api.example.com/another').json()
  ];

  const results = await Promise.allSettled(requests);

  const successful = results
    .filter(result => result.status === 'fulfilled')
    .map(result => result.value);

  const failed = results
    .filter(result => result.status === 'rejected')
    .map(result => result.reason);

  console.log('成功:', successful.length);
  console.log('失敗:', failed.length);

  return { successful, failed };
}

// ページネーション対応データ取得
async function fetchAllPages(baseUrl) {
  const allData = [];
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    try {
      const pageData = await got.get(baseUrl, {
        searchParams: {
          page: page,
          limit: 20
        }
      }).json();

      allData.push(...pageData.items);
      hasMore = pageData.hasMore;
      page++;

      console.log(`Page ${page - 1} 取得: ${pageData.items.length}件`);
      
      // API負荷軽減
      if (hasMore) {
        await new Promise(resolve => setTimeout(resolve, 100));
      }
    } catch (error) {
      console.error(`Page ${page} エラー:`, error);
      break;
    }
  }

  return allData;
}

// レート制限対応リクエスト
async function rateLimitedRequests(urls, concurrency = 3) {
  const results = [];
  
  for (let i = 0; i < urls.length; i += concurrency) {
    const batch = urls.slice(i, i + concurrency);
    
    const batchResults = await Promise.allSettled(
      batch.map(url => got.get(url).json())
    );
    
    results.push(...batchResults);
    
    // バッチ間の待機
    if (i + concurrency < urls.length) {
      await new Promise(resolve => setTimeout(resolve, 1000));
    }
  }
  
  return results;
}

// 重複リクエスト防止
const pendingRequests = new Map();

async function deduplicatedRequest(url) {
  if (pendingRequests.has(url)) {
    return pendingRequests.get(url);
  }
  
  const promise = got.get(url).json();
  pendingRequests.set(url, promise);
  
  try {
    const result = await promise;
    return result;
  } finally {
    pendingRequests.delete(url);
  }
}

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

// Express.jsでのプロキシサーバー
import express from 'express';
import got from 'got';

const app = express();

app.get('/api/proxy/*', async (req, res) => {
  try {
    const targetUrl = `https://external-api.example.com${req.path.replace('/api/proxy', '')}`;
    
    const response = await got.get(targetUrl, {
      searchParams: req.query,
      headers: {
        'Authorization': req.headers.authorization
      }
    });

    res.set(response.headers);
    res.send(response.body);
  } catch (error) {
    res.status(error.response?.statusCode || 500);
    res.json({ error: error.message });
  }
});

// Fastifyプラグインとしての統合
import fastify from 'fastify';

const server = fastify();

server.register(async function (fastify) {
  const apiClient = got.extend({
    prefixUrl: 'https://api.example.com',
    timeout: { request: 10000 }
  });
  
  fastify.decorate('apiClient', apiClient);
});

server.get('/users/:id', async (request, reply) => {
  try {
    const user = await request.server.apiClient.get(`users/${request.params.id}`).json();
    return user;
  } catch (error) {
    reply.status(error.response?.statusCode || 500);
    return { error: error.message };
  }
});

// ストリーミングファイルアップロード
import fs from 'node:fs';
import { pipeline } from 'node:stream/promises';

async function uploadFile(filePath, uploadUrl) {
  try {
    await pipeline(
      fs.createReadStream(filePath),
      got.stream.post(uploadUrl, {
        headers: {
          'Content-Type': 'application/octet-stream'
        }
      })
    );
    
    console.log('ファイルアップロード完了');
  } catch (error) {
    console.error('アップロードエラー:', error);
    throw error;
  }
}

// ストリーミングファイルダウンロード
async function downloadFile(url, outputPath) {
  try {
    await pipeline(
      got.stream(url),
      fs.createWriteStream(outputPath)
    );
    
    console.log('ファイルダウンロード完了');
  } catch (error) {
    console.error('ダウンロードエラー:', error);
    throw error;
  }
}

// 認証トークン自動更新機能
class AuthenticatedClient {
  constructor(baseUrl, credentials) {
    this.baseUrl = baseUrl;
    this.credentials = credentials;
    this.token = null;
    this.tokenExpiry = null;
    
    this.client = got.extend({
      prefixUrl: baseUrl,
      hooks: {
        beforeRequest: [
          async (options) => {
            await this.ensureValidToken();
            options.headers.authorization = `Bearer ${this.token}`;
          }
        ],
        afterResponse: [
          (response, retryWithMergedOptions) => {
            if (response.statusCode === 401) {
              this.token = null; // トークンをリセット
              return retryWithMergedOptions();
            }
            return response;
          }
        ]
      }
    });
  }
  
  async ensureValidToken() {
    if (this.token && this.tokenExpiry && Date.now() < this.tokenExpiry) {
      return; // 有効なトークンが存在
    }
    
    await this.refreshToken();
  }
  
  async refreshToken() {
    const response = await got.post(`${this.baseUrl}/auth/token`, {
      json: this.credentials
    }).json();
    
    this.token = response.access_token;
    this.tokenExpiry = Date.now() + (response.expires_in * 1000);
  }
  
  async get(url, options = {}) {
    return this.client.get(url, options);
  }
  
  async post(url, options = {}) {
    return this.client.post(url, options);
  }
}

// 使用例
const authClient = new AuthenticatedClient('https://api.example.com', {
  username: 'user',
  password: 'pass'
});

const userData = await authClient.get('users/me').json();
const newPost = await authClient.post('posts', {
  json: { title: 'タイトル' }
}).json();