uuid v14 で v4 / v7 を生成して検証する
uuid v14 系の API(v4 ランダム / v7 時刻 prefix / validate / version)を最小コードと触れる demo で確認する実装メモ。Node 18+ / ブラウザ標準 crypto.randomUUID() との使い分け、v7 timestamp leak のセキュリティ注意点、@types/uuid deprecated の移行も。
検証日: 2026-05-26
使用バージョン:
uuid@14.0.0(2026-05 時点の最新メジャー)対象: ID をクライアント側で生成したい / DB の主キーを v7 で時系列ソート可能にしたい
uuid v14 の API(v4(ランダム)/ v7(時刻 prefix 付き)/ validate / version)を最小コードで使うパターン。v4 と v7 の使い分け、ブラウザ標準 crypto.randomUUID() との関係を動く demo で確認します。
触って試す
v4(ランダム)と v7(時刻 prefix)のどちらを生成するかと、貼り付けた UUID の検証が即座に確認できる。
セットアップ(npm 1 行 / Node 18+ / 型同梱)
npm i uuid
# or
pnpm add uuid
| 項目 | 値 |
|---|---|
| 公開バージョン | 14.0.0(2026-05) |
| Node 必要バージョン | 18+ (v10 で crypto.randomUUID を利用) |
| TypeScript 型 | 本体同梱(v10 以降)、@types/uuid は deprecated |
| ブラウザ対応 | secure context(https / localhost)で crypto.getRandomValues を使用 |
| 公開バンドル | ESM / CommonJS / browser bundle 同梱、tree-shake 可 |
@types/uuid を入れている既存プロジェクトの移行:npm rm @types/uuid してから本体 uuid を最新に上げると import { v4 } from "uuid" の型が解決される。両方入っていると型が重複して衝突するので、片方を必ず外す。
API 最小サンプル
v4 / v7 を import して 1 行で生成。validate で形式チェック、version でバージョン番号を取得:
import { v4 as uuidv4, v7 as uuidv7, validate, version } from "uuid";
const id = uuidv4(); // ランダム: "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed"
const ord = uuidv7(); // 時刻 prefix: "0192a07e-..." 先頭が時系列
validate(id); // true
version(id); // 4
v4 と v7 の使い分け
| 軸 | v4 | v7 |
|---|---|---|
| 生成方式 | 完全ランダム | 先頭 48bit が UNIX ms |
| 時系列ソート可能 | × | ○ |
| インデックス局所性(B-tree) | △(ランダムで断片化) | ○(挿入が末尾に集まる) |
| 衝突確率 | 実質ゼロ(122bit ランダム) | 実質ゼロ(74bit ランダム + 時刻) |
DB の主キー候補なら v7 が現代的選択。クライアント側のローカル ID には v4 で十分。
ブラウザ環境(crypto.randomUUID() との関係)
ブラウザには標準で crypto.randomUUID() がある(v4 のみ生成)。
crypto.randomUUID(); // "..." (v4 相当、import 不要)
v4 だけでよく、ブラウザ環境のみであれば追加 dep 不要。uuid パッケージは:
- v7 / v6 / v3 / v5 など他バージョンが必要
- Node + browser 双方で動かしたい
validate/versionなどのユーティリティが欲しい
時に検討する。
v7 の timestamp leak — 公開 ID として出す時の注意
v7 は 先頭 48bit が UNIX ms なので、URL に出す ID から「いつ作成されたか」が逆算できる。これは次のような場面で意図しない情報露出になる:
- 招待リンクの URL に v7 を埋める → 招待が作成された時刻が外部から推測できる
- 抽選 ID / 申込番号 → 申込順序や殺到時間がバレる
- セキュリティトークン → 推測可能なので NG(v4 を使う)
「公開 URL に載る ID は v4 / 内部 DB の primary key は v7」 という二段構えが現実的。セキュリティトークンに v7 や v4 を使わない:crypto.randomBytes(32).toString("base64url") 等の高エントロピー値を別途生成する。
4 つの落とし穴と直し方
| 症状 | 原因 | 直し方 |
|---|---|---|
import { v3 } from "uuid" で uuidv3(name) が TypeError | v3 / v5 は namespace 必須 | uuidv3(name, NAMESPACE_DNS) のように uuid.NIL 以外の namespace UUID を渡す |
validate(id) は true なのに version(id) で undefined | validate は 形式のみ 確認 | version 取得は version() を別に呼ぶ。両者の役割は分離 |
既存コードの import { v4 } from "uuid/v4" が動かない | v9 以前のサブパス import 廃止 | import { v4 } from "uuid" に書き換え |
@types/uuid 入れたら型が二重定義で衝突 | v10+ は 本体に型同梱 | @types/uuid を npm rm、本体だけ最新化 |
v7 ソートと DB index — 何が嬉しいか
PostgreSQL の uuid 型は バイト列でソートされる。v7 は先頭 48bit が単調増加するので、B-tree index の挿入が末尾に集中し、ページ分割が起きにくい。v4 はランダムなので index のあちこちに挿入が散らばる(write amplification)。
- v4 を主キーにすると、行が増えるほど書き込みが index 全域にばらつく
- v7 を主キーにすると、新しい行は index の末尾ページに連続挿入される(BIGSERIAL に近い挙動だが、衝突しない分散 ID)
「BIGSERIAL は嫌(分散 DB / 採番サーバ依存) / でも index 局所性は欲しい」というケースで v7 が刺さる。MySQL の BINARY(16) 格納でも同じ理屈(CHAR(36) 格納だと 36 文字の文字列比較になり 2 倍以上 index が膨らむので注意)。
評価
| 観点 | 評価 |
|---|---|
| 学習コスト | ◎ |
| 型サポート | ◎(本体に同梱) |
| バンドルサイズ | ○(tree-shake で必要分のみ) |
関連 Topic / 関連書籍
この記事と関係する tech-book.net の Topic と、それぞれの Topic に紐づく書籍:
JavaScriptによるはじめてのアルゴリズム入門 | tech-book.net
関連テーマ・学習ロードマップから次の一冊へ。
Python と JavaScriptではじめるデータビジュアライゼーション | tech-book.net
関連テーマ・学習ロードマップから次の一冊へ。
React Native+Expoではじめるスマホアプリ開発 : JavaScriptによるアプリ構築の実際 | tech-book.net
関連テーマ・学習ロードマップから次の一冊へ。
はじめてのWebデザイン&プログラミング : HTML、CSS、JavaScript、PHPの基本 | tech-book.net
関連テーマ・学習ロードマップから次の一冊へ。
フロントエンドの知識地図ーー 一冊でHTML/CSS/JavaScriptの開発技術が学べる本 | tech-book.net
関連テーマ・学習ロードマップから次の一冊へ。