Got
Node.js TypeScript環境向けの高性能HTTPクライアント。完全なTypeScript型定義、Axiosと同等の豊富な機能を提供しながら優れたパフォーマンスを実現。ストリーム対応、高度なエラーハンドリング、自動リトライ、キャッシュ機能を内蔵。
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
スター履歴
データ取得日時: 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();