NodeJS 速習チュートリアル

Node.js Util モジュール

1. Util モジュールとは?

Util モジュールは、Node.js 開発で頻繁に発生するタスクをサポートするためのユーティリティ関数を集めたコアモジュールです。
開発者にとっては「スイスアーミーナイフ(万能ナイフ)」のような存在であり、以下のようなソリューションを提供します。

1.1 主なユースケース

  • プレースホルダーを使用した文字列のフォーマット
  • デバッグのためのオブジェクトのインスペクション(中身の可視化)
  • コールバックと Promise の相互変換
  • 高度な型判定とバリデーション
  • 非推奨(Deprecation)警告のハンドリング
  • デバッグログの出力

1.2 主なメリット

  • 外部依存関係(ライブラリ)が不要
  • パフォーマンスが最適化されている
  • Node.js コアと親和性が高い
  • 開発・デバッグからプロダクション環境まで幅広く活用可能

       メモ: Util モジュール内の一部の関数は Node.js 内部向けに設計されていますが、多くはアプリケーション開発者にとって非常に有用なツールです。Node.js に標準搭載されているため、インストール作業は不要です。

2. Util を使い始める

まずは、Util モジュールの代表的な機能をいくつか組み合わせた実践的なコード例を見てみましょう。

2.1 基本的な使用例

const util = require('util');
const fs = require('fs');

// コールバックベースの fs.readFile を Promise ベースに変換
const readFile = util.promisify(fs.readFile);

// プレースホルダーを使用して文字列をフォーマット
const greeting = util.format('こんにちは、%sさん! 本日は %s です', '開発者', new Date().toDateString());
console.log(greeting);

// オブジェクトをカスタムオプションでインスペクション
const obj = {
  name: 'テスト',
  nested: { a: 1, b: [2, 3] },
  fn: function() { return 'test'; }
};
console.log(util.inspect(obj, { colors: true, depth: 2 }));

// デバッグ用ログ関数の作成
const debug = util.debuglog('app');
debug('NODE_DEBUG=app が設定されている場合のみ表示されます');

// promisify を async/await と組み合わせて使用する例
async function readConfig() {
  try {
    const data = await readFile('package.json', 'utf8');
    console.log('パッケージ名:', JSON.parse(data).name);
  } catch (err) {
    console.error('設定ファイルの読み込みエラー:', err);
  }
}
readConfig();

3. インポートとセットアップ

利用環境やモジュールシステムに合わせて、いくつかの方法でインポートできます。

3.1 CommonJS (Node.js デフォルト)

// モジュール全体をインポート
const util = require('util');

// 分割代入を使用して特定の関数のみをインポート
const { promisify, inspect, format } = require('util');

// TypeScript ユーザーの場合
// import * as util from 'util';
// import { promisify, inspect } from 'util';

3.2 ES Modules (Node.js 12+)

// デフォルトインポート
import util from 'util';

// 名前付きインポート
import { promisify, inspect } from 'util';

// 別名を付けてインポート
import { promisify as pify } from 'util';

// 動的インポート (Node.js 14+)
const { promisify } = await import('util');

ベストプラクティス: ツリーシェイキング(Tree-shaking)を最適化し、バンドルサイズを抑えるために、必要な関数のみを分割代入でインポートすることをお勧めします。

4. 文字列のフォーマットとインスペクション

ロギングやデバッグにおいて、文字列の整形やオブジェクトの中身の確認は不可欠です。

4.1 util.format(format[, ...args])

最初の引数を printf 風のフォーマット文字列として使用し、整形された文字列を返します。
console.log() と似ていますが、標準出力に表示するのではなく「文字列として返す」点が異なります。

フォーマット指定子(Format Specifiers):

  • %s - 文字列
  • %d - 数値(整数および浮動小数点数)
  • %i - 整数
  • %f - 浮動小数点数
  • %j - JSON(循環参照がある場合は '[Circular]' に置換)
  • %o - オブジェクト(インスペクション結果を表示)
  • %O - オブジェクト(詳細なインスペクション結果を表示)
  • %% - 単一のパーセント記号 ('%')
const util = require('util');

// 基本的なフォーマット
const formatted = util.format('こんにちは、%s!', '世界');
console.log(formatted); // 'こんにちは、世界!'

// 複数のプレースホルダー
const multiFormatted = util.format(
  '私の名前は %s です。年齢は %d 歳で、%s を愛しています。',
  'カイ',
  30,
  'Node.js'
);
console.log(multiFormatted);
// '私の名前は カイ です。年齢は 30 歳で、Node.js を愛しています。'

// 余分な引数はスペースで連結される
const extra = util.format('Hello', 'World', 'from', 'Node.js');
console.log(extra); // 'Hello World from Node.js'

5. util.inspect(object[, options])

オブジェクトを文字列に変換します。デバッグに非常に便利です。Node.js が内部で console.log() を実行する際にも使用されています。

主なオプション:

  • showHidden - 非列挙プロパティを表示(デフォルト: false)
  • depth - 再帰的にプロパティを表示する深さ(デフォルト: 2、無制限にする場合は null)
  • colors - ANSI カラーコードを追加(デフォルト: false)
  • sorted - プロパティをアルファベット順にソート
const util = require('util');

const obj = {
  name: 'John',
  age: 30,
  hobbies: ['読書', 'コーディング'],
  address: {
    city: 'Tokyo',
    country: 'Japan'
  }
};

// カスタムオプションで出力
console.log(util.inspect(obj, {
  colors: true,
  depth: 0,
  compact: false,
  sorted: true
}));

// 循環参照のハンドリング
const circular = { name: '循環参照' };
circular.self = circular;
console.log(util.inspect(circular)); // { name: '循環参照', self: [Circular] }

6. util.inspect.custom

オブジェクトのインスペクション結果をカスタマイズするためのシンボルです。これを定義することで、そのオブジェクトがインスペクトされた際の出力を制御できます。

const util = require('util');

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
    this._private = '隠したい情報';
  }
  
  // カスタムインスペクトメソッド
  [util.inspect.custom](depth, options) {
    return `Person(${this.name}, ${this.age})`;
  }
}
const kai = new Person('カイ', 30);

console.log(util.inspect(kai)); // Person(カイ, 30)
console.log(kai); // Person(カイ, 30)

7. Promise と非同期ユーティリティ

Node.js の非同期プログラミングをよりスムーズにするためのメソッドです。

7.1 util.promisify(original)

Node.js の標準的なコールバックパターン(error-first 形式)を持つ関数を、Promise を返す関数に変換します。

const util = require('util');
const fs = require('fs');

const readFilePromise = util.promisify(fs.readFile);

async function readFileExample() {
  try {
    const data = await readFilePromise('package.json', 'utf8');
    console.log('ファイル内容の冒頭:', data.substring(0, 100) + '...');
  } catch (err) {
    console.error('読み込み失敗:', err.message);
  }
}
readFileExample();

7.2 util.callbackify(original)

Promise を返す関数を、コールバック形式の関数に変換します。古いライブラリとの互換性を保つ必要がある場合に便利です。

const util = require('util');

async function fetchUserData(id) {
  if (!id) throw new Error('IDが必要です');
  return { id, name: `ユーザー ${id}` };
}

const fetchUserDataCallback = util.callbackify(fetchUserData);

fetchUserDataCallback(1, (err, user) => {
  if (err) return console.error(err);
  console.log('ユーザーデータ:', user);
});

8. 型判定ユーティリティ

util.types は、JavaScript の組み込みオブジェクトや Node.js 固有のオブジェクトに対して、typeof よりも正確な型判定を提供します。

const util = require('util');

// 各種判定例
console.log(util.types.isDate(new Date()));        // true
console.log(util.types.isRegExp(/test/));          // true
console.log(util.types.isPromise(Promise.resolve())); // true
console.log(util.types.isUint8Array(new Uint8Array())); // true
console.log(util.types.isProxy(new Proxy({}, {}))); // true

9. 非推奨化ユーティリティ

API の移行を安全に行うための機能です。

9.1 util.deprecate(fn, msg[, code])

関数を「非推奨」としてマークし、呼び出された際に警告を表示します。

const util = require('util');

function oldFunction(x, y) {
  return x + y;
}

const deprecatedFunction = util.deprecate(
  oldFunction,
  'oldFunction() は非推奨です。代わりに newFunction() を使用してください。',
  'DEP0001'
);

// 実行時に警告が表示される
console.log('結果:', deprecatedFunction(5, 10));

10. デバッグと開発ユーティリティ

10.1 util.debuglog(section)

環境変数 NODE_DEBUG の値に基づいて、条件付きで標準エラー出力(stderr)にメッセージを書き出す関数を作成します。

// 実行例: NODE_DEBUG=app,db node server.js

const util = require('util');
const debugApp = util.debuglog('app');
const debugDB = util.debuglog('db');

debugApp('アプリケーションが起動しました。設定: %j', { env: 'production' });
debugDB('データベースに接続しました: %s', 'postgres://localhost:5432');

ベストプラクティス:

  • セクション名にはモジュール名など、意味のある名前を付ける。
  • プロダクション環境で不要なログが出力されないよう、適切に環境変数を管理する。