undici の使い方

An HTTP/1.1 client, written from scratch for Node.js

v8.0.267.8M/週MITHTTPクライアント
AI生成コンテンツ

この記事はAIによって生成されました。内容の正確性は保証されません。最新の情報は公式ドキュメントをご確認ください。

undici の使い方 — Node.js のための高性能 HTTP/1.1 クライアント

一言でいうと

undici は、Node.js のためにゼロから書き直された高性能な HTTP/1.1 クライアントです。Node.js v18+ に組み込まれている fetch() の内部エンジンでもあり、単体モジュールとしてインストールすることで、requeststreampipeline などの高速APIやコネクションプーリングの細かな制御が可能になります。

どんな時に使う?

  1. 高パフォーマンスが求められる API 通信undici.request() は標準の fetch()axios と比較して数倍のスループットを発揮します。マイクロサービス間通信やバッチ処理など、大量のHTTPリクエストを捌く場面に最適です。
  2. プロキシ経由・モック付きのHTTP通信ProxyAgentSocks5AgentMockAgent など、組み込み fetch では利用できない高度な機能が必要な場合に使います。
  3. テスト環境でのHTTPモックMockAgent を使えば、外部ライブラリなしでHTTPリクエストをインターセプト・モックでき、テストの信頼性と速度が向上します。

インストール

# npm
npm install undici

# yarn
yarn add undici

# pnpm
pnpm add undici

Note: Node.js v18+ にはundiciベースの fetch() が組み込まれていますが、モジュールとして別途インストールすることで最新機能・最新パフォーマンス改善を利用できます。

基本的な使い方

最もよく使うパターンは request() による高速なHTTPリクエストです。

import { request } from 'undici';

// GET リクエスト
async function fetchUser(userId: string) {
  const { statusCode, headers, body } = await request(`https://api.example.com/users/${userId}`);

  if (statusCode !== 200) {
    // body は必ず消費する必要がある(後述の注意点参照)
    await body.dump();
    throw new Error(`Request failed with status ${statusCode}`);
  }

  const data = await body.json() as { id: string; name: string; email: string };
  console.log(data);
  return data;
}

// POST リクエスト(JSON)
async function createUser(name: string, email: string) {
  const { statusCode, body } = await request('https://api.example.com/users', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ name, email }),
  });

  const result = await body.json();
  return { statusCode, result };
}

よく使う API — undici の使い方を API 別に解説

1. request() — 最速の汎用リクエスト

undici で最もパフォーマンスが高い API です。レスポンスを statusCodeheadersbody に分解して返します。

import { request } from 'undici';

// クエリパラメータ付き GET
const { statusCode, headers, body } = await request('https://api.example.com/search', {
  method: 'GET',
  query: {
    q: 'undici',
    page: '1',
  },
  headers: {
    Authorization: 'Bearer my-token',
  },
  headersTimeout: 5000,   // ヘッダー受信までのタイムアウト(ms)
  bodyTimeout: 30000,      // ボディ受信までのタイムアウト(ms)
});

const text = await body.text();
console.log(`Status: ${statusCode}`);
console.log(`Content-Type: ${headers['content-type']}`);
console.log(text);

2. fetch() — Web 標準互換の Fetch API

ブラウザの fetch() と同じインターフェースです。組み込み fetch のドロップイン置き換えとして使えます。

import { fetch, Headers } from 'undici';

const headers = new Headers({
  'Content-Type': 'application/json',
  'Accept': 'application/json',
});

const response = await fetch('https://api.example.com/data', {
  method: 'POST',
  headers,
  body: JSON.stringify({ key: 'value' }),
});

if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}

const data = await response.json();
console.log(data);

3. Agent / setGlobalDispatcher() — コネクションプーリングの制御

コネクションの再利用やタイムアウトをきめ細かく設定できます。

import { Agent, setGlobalDispatcher, request } from 'undici';

// カスタム Agent を作成
const agent = new Agent({
  keepAliveTimeout: 10_000,      // Keep-Alive タイムアウト(ms)
  keepAliveMaxTimeout: 30_000,   // Keep-Alive 最大タイムアウト(ms)
  connections: 100,              // オリジンあたりの最大コネクション数
  pipelining: 10,                // パイプライニング深度
});

// グローバルに適用(以降の request / fetch すべてに影響)
setGlobalDispatcher(agent);

// または個別リクエストで dispatcher を指定
const { body } = await request('https://api.example.com/data', {
  dispatcher: agent,
});
const data = await body.json();

4. MockAgent — テスト用 HTTP モック

外部サービスへのリクエストをインターセプトしてモックレスポンスを返します。

import { MockAgent, setGlobalDispatcher, request } from 'undici';

// MockAgent を作成してグローバルに設定
const mockAgent = new MockAgent();
setGlobalDispatcher(mockAgent);

// 実際のネットワーク接続を無効化
mockAgent.disableNetConnect();

// モックプールを取得し、インターセプトを設定
const mockPool = mockAgent.get('https://api.example.com');

mockPool.intercept({
  path: '/users/1',
  method: 'GET',
}).reply(200, {
  id: '1',
  name: 'Test User',
  email: 'test@example.com',
}, {
  headers: { 'Content-Type': 'application/json' },
});

// モックされたレスポンスが返る
const { statusCode, body } = await request('https://api.example.com/users/1');
const data = await body.json();
console.log(statusCode); // 200
console.log(data);       // { id: '1', name: 'Test User', email: 'test@example.com' }

// テスト後のクリーンアップ
await mockAgent.close();

5. ProxyAgent — プロキシ経由のリクエスト

HTTP/HTTPS プロキシを経由してリクエストを送信します。

import { ProxyAgent, setGlobalDispatcher, request } from 'undici';

const proxyAgent = new ProxyAgent('http://proxy.example.com:8080');
setGlobalDispatcher(proxyAgent);

// 以降のリクエストはすべてプロキシ経由
const { statusCode, body } = await request('https://api.example.com/data');
const data = await body.json();
console.log(statusCode, data);

// 個別リクエストでプロキシを指定することも可能
const { body: body2 } = await request('https://other-api.example.com', {
  dispatcher: proxyAgent,
});

類似パッケージとの比較

特徴undiciaxiosnode-fetchgot
Node.js 公式✅(Node.js コアに組み込み)
パフォーマンス⚡ 非常に高速普通普通やや高速
HTTP/1.1 パイプライニング
Fetch API 互換
組み込みモックMockAgent❌(別途 mock ライブラリ必要)
プロキシサポートProxyAgent❌(別途必要)
ブラウザ対応❌(Node.js 専用)
リトライ機能インターセプターで実装❌(別途必要)✅ 組み込み
TypeScript 型定義✅ 同梱✅ 同梱✅(@types✅ 同梱
依存パッケージ数0数個数個多め

注意点・Tips

1. レスポンスボディは必ず消費する

request() で取得したボディを読まずに放置すると、コネクションがリークします。エラー時でも必ずボディを消費してください。

import { request } from 'undici';

const { statusCode, body } = await request('https://api.example.com/data');

if (statusCode !== 200) {
  // ボディを読み捨てる(これを忘れるとコネクションリーク)
  await body.dump();
  throw new Error(`Unexpected status: ${statusCode}`);
}

const data = await body.json();

2. fetchFormData は同じ実装から揃える

undici の fetch とグローバルの FormData を混在させると、互換性の問題が発生します。必ず同じ出所から import してください。

// ✅ 正しい — 両方 undici から import
import { fetch, FormData } from 'undici';

const form = new FormData();
form.set('file', new Blob(['hello']), 'hello.txt');
await fetch('https://example.com/upload', { method: 'POST', body: form });

// ❌ 間違い — グローバル FormData と undici fetch を混在
// import { fetch } from 'undici';
// const form = new FormData(); // ← グローバルの FormData
// await fetch('...', { body: form }); // 動作が不安定になる可能性

3. request()fetch() の使い分け

  • パフォーマンス最優先request() を使う(ベンチマークで fetch() の約3倍速い場合も)
  • Web 標準互換・ポータビリティ重視fetch() を使う
  • ストリーム処理stream() を使う

4. タイムアウトの設定

デフォルトではタイムアウトが長めに設定されています。本番環境では明示的に設定しましょう。

import { request } from 'undici';

const { body } = await request('https://api.example.com/data', {
  headersTimeout: 5_000,  // 5秒以内にヘッダーが届かなければエラー
  bodyTimeout: 30_000,    // 30秒以内にボディ受信が完了しなければエラー
  signal: AbortSignal.timeout(60_000), // 全体のタイムアウト
});

5. 組み込み fetch との共存

process.versions.undici でNode.jsにバンドルされているundiciのバージョンを確認できます。モジュール版をインストールすると、バンドル版より新しい機能を使えます。

// Node.js にバンドルされている undici のバージョンを確認
console.log(process.versions.undici); // 例: "6.21.1"

6. install() でグローバルを置き換える

undici モジュールの実装でグローバルの fetch / FormData 等を一括置換できます。

import { install } from 'undici';

// グローバルの fetch, Headers, Request, Response, FormData を undici 版に置換
install();

// 以降はグローバル fetch が undici の最新版になる
const res = await fetch('https://api.example.com/data');

まとめ

undici は Node.js 公式の HTTP クライアントエンジンであり、request() API を使えば axiosnode-fetch を大幅に上回るパフォーマンスを発揮します。MockAgent によるテストモック、ProxyAgent によるプロキシ対応、コネクションプーリングの細かな制御など、本番運用に必要な機能が依存ゼロで揃っています。Node.js でHTTP通信を行うなら、まず undici を検討すべき選択肢です。