Skip to content

Latest commit

 

History

History
463 lines (342 loc) · 16.9 KB

File metadata and controls

463 lines (342 loc) · 16.9 KB

Nano ID

Nano ID logo by Anton Lovchikov

English | 日本語 | Русский | 简体中文 | Bahasa Indonesia | 한국어 | العربية

JavaScriptのための小さく、安全で、URL友好的なユニークな文字列ID生成器。

「意味不明なレベルの完璧主義、 これは尊敬せざるを得ない。」

  • 小さい。 118バイト(圧縮・brotli圧縮後)。依存関係なし。 Size Limitがサイズを管理。
  • 安全。 ハードウェア乱数生成器を使用。クラスタでも利用可能。
  • 短いID。 UUIDより大きなアルファベット(A-Za-z0-9_-)を使用。 そのためID長は36から21文字に短縮されています。
  • 移植性。 Nano IDは20以上のプログラミング言語に移植されています。
import { nanoid } from 'nanoid'
model.id = nanoid() //=> "V1StGXR8_Z5jdHi6B-myT"

Made at Evil Martians, product consulting for developer tools.


目次

UUIDとの比較

Nano IDはUUID v4(ランダムベース)と十分に比較可能です。 ID内のランダムビット数は同様です(Nano IDで126ビット、UUIDで122ビット)、 そのため衝突確率も同様です:

10億分の1の確率で重複が発生するには、 103兆個のバージョン4 IDを生成する必要があります。

Nano IDとUUID v4の主な違いは2つあります:

  1. Nano IDはより大きなアルファベットを使用するため、同様のランダムビット数が 36文字ではなく21文字に詰め込まれています。
  2. Nano IDのコードはuuid/v4パッケージより4倍小さいです: 423バイトではなく118バイトです。

ベンチマーク

$ node ./test/benchmark.js
crypto.randomUUID       21,741,317 ops/sec
uuid v4                 21,204,378 ops/sec
@napi-rs/uuid           10,236,615 ops/sec
uid/secure              10,567,676 ops/sec
@lukeed/uuid             8,647,481 ops/sec
nanoid                   7,800,308 ops/sec
customAlphabet           9,697,350 ops/sec
nanoid for browser         576,759 ops/sec
secure-random-string       529,253 ops/sec
uid-safe.sync              526,459 ops/sec

Non-secure:
uid                     31,379,525 ops/sec
nanoid/non-secure        3,678,505 ops/sec
rndm                     3,767,185 ops/sec

テスト構成:Framework 13 7840U, Fedora 39, Node.js 21.6.

セキュリティ

乱数生成器の理論に関する良い記事を参照してください: Secure random values (in Node.js)

  • 予測不可能性。 安全でないMath.random()を使う代わりに、Nano IDは Node.jsではcryptoモジュールを、ブラウザではWeb Crypto APIを使用します。 これらのモジュールは予測不可能なハードウェア乱数生成器を使用します。

  • 均一性。 random % alphabetはID生成器をコーディングする際によくある間違いです。 分布は均一ではなく、一部の記号が他のものより出現確率が低くなります。 そのため、総当たり攻撃の際に試行回数が減少します。Nano IDはより良いアルゴリズムを 使用し、均一性についてテストされています。

    Nano ID uniformity

  • 十分に文書化されている: Nano IDのすべてのハックは文書化されています。 ソースのコメントを参照してください。

  • 脆弱性: セキュリティの脆弱性を報告するには、 Tideliftセキュリティ連絡先を使用してください。 Tideliftが修正と開示を調整します。

インストール

ESM

Nano ID 5はESMプロジェクト(importを使用)のテストやNode.jsスクリプトで動作します。

npm install nanoid

CommonJS

Nano IDは以下のいずれかの方法でCommonJSで使用できます:

  • require()を使用してNano IDをインポートできます。最新のNode.js 22.12 (標準で動作)またはNode.js 20(--experimental-require-moduleオプション付き)が必要です。

  • Node.js 18では、次のようにNano IDを動的にインポートできます:

    let nanoid
    module.exports.createID = async () => {
      if (!nanoid) ({ nanoid } = await import('nanoid'))
      return nanoid() // => "V1StGXR8_Z5jdHi6B-myT"
    }
  • Nano ID 3.xを使用できます(まだサポートしています):

    npm install nanoid@3

JSR

JSRはオープンなガバナンスと積極的な開発(npmとは対照的に)を持つnpmの代替です。

npx jsr add @sitnik/nanoid

Node.js、Deno、Bunなどで使用できます。

// すべてのインポートで`nanoid`を`@sitnik/nanoid`に置き換える
import { nanoid } from '@sitnik/nanoid'

Denoでは、deno add jsr:@sitnik/nanoidでインストールするか、 jsr:@sitnik/nanoidからインポートします。

CDN

クイックハックの場合、CDNからNano IDを読み込むことができます。ただし、読み込みパフォーマンスが低いため、本番環境での使用はお勧めしません。

import { nanoid } from 'https://cdn.jsdelivr.net/npm/nanoid/nanoid.js'

API

Nano IDには2つのAPI:通常と非セキュアがあります。

デフォルトでは、Nano IDはURL友好的な記号(A-Za-z0-9_-)を使用し、 21文字のID(UUID v4と同様の衝突確率を持つ)を返します。

ブロッキング

Nano IDを使用する安全で最も簡単な方法です。

まれに、ハードウェア乱数生成器のノイズ収集中にCPUを他の作業からブロックする場合があります。

import { nanoid } from 'nanoid'
model.id = nanoid() //=> "V1StGXR8_Z5jdHi6B-myT"

IDのサイズを小さくしたい場合(衝突確率を高める)、 サイズを引数として渡すことができます。

nanoid(10) //=> "IRFa-VaY2b"

IDサイズの安全性をID衝突確率計算機で確認することを忘れないでください。

カスタムアルファベットランダム生成器も使用できます。

非セキュア

デフォルトでは、Nano IDはセキュリティと低衝突確率のためにハードウェアランダムバイト生成を使用します。セキュリティにそれほど関心がない場合は、ハードウェア乱数生成器がない環境でも使用できます。

import { nanoid } from 'nanoid/non-secure'
const id = nanoid() //=> "Uakgb_J5m9g-0JDMbcJqLJ"

カスタムアルファベットまたはサイズ

customAlphabetは、独自のアルファベットとIDサイズでnanoidを作成できる関数を返します。

import { customAlphabet } from 'nanoid'
const nanoid = customAlphabet('1234567890abcdef', 10)
model.id = nanoid() //=> "4f90d13a42"
import { customAlphabet } from 'nanoid/non-secure'
const nanoid = customAlphabet('1234567890abcdef', 10)
user.id = nanoid()

カスタムアルファベットとIDサイズの安全性をID衝突確率計算機で確認してください。 より多くのアルファベットについては、nanoid-dictionaryのオプションを確認してください。

アルファベットは256記号以下でなければなりません。 そうでない場合、内部生成アルゴリズムのセキュリティは保証されません。

デフォルトサイズを設定するだけでなく、関数を呼び出す際にIDサイズを変更することもできます:

import { customAlphabet } from 'nanoid'
const nanoid = customAlphabet('1234567890abcdef', 10)
model.id = nanoid(5) //=> "f01a2"

カスタムランダムバイト生成器

customRandomを使用すると、nanoidを作成し、アルファベットとデフォルトのランダムバイト生成器を置き換えることができます。

この例では、シードベースの生成器が使用されています:

import { customRandom } from 'nanoid'

const rng = seedrandom(seed)
const nanoid = customRandom('abcdef', 10, size => {
  return new Uint8Array(size).map(() => 256 * rng())
})

nanoid() //=> "fbaefaadeb"

randomコールバックは配列サイズを受け取り、ランダムな数値の配列を返す必要があります。

customRandomで同じURL友好的な記号を使用したい場合は、urlAlphabetを使用してデフォルトのアルファベットを取得できます。

const { customRandom, urlAlphabet } = require('nanoid')
const nanoid = customRandom(urlAlphabet, 10, random)

なお、Nano IDのバージョン間でランダム生成器の呼び出しシーケンスが変更される場合があります。シードベースの生成器を使用している場合、同じ結果を保証するものではありません。

使用方法

React

Reactのkey propにNano IDを使用する正しい方法はありません。なぜなら、keyはレンダー間で一貫性がある必要があるからです。

function Todos({ todos }) {
  return (
    <ul>
      {todos.map(todo => (
        <li key={nanoid()}>
          {' '}
          /* これはやめましょう */
          {todo.text}
        </li>
      ))}
    </ul>
  )
}

代わりに、リストアイテム内で安定したIDを使用するようにしましょう。

const todoItems = todos.map(todo => <li key={todo.id}>{todo.text}</li>)

安定したIDがない場合は、nanoid()の代わりにインデックスをkeyとして使用することをお勧めします:

const todoItems = todos.map((text, index) => (
  <li key={index}>
    {' '}
    /* まだ推奨されませんが、nanoid()よりは優先されます。
    アイテムに安定したIDがない場合のみ行ってください。 */
    {text}
  </li>
))

ラベルと入力フィールドのように要素を関連付けるためだけにランダムなIDが必要な場合は、useIdが推奨されます。 このフックはReact 18で追加されました。

React Native

React Nativeには組み込みのランダム生成器がありません。次のポリフィルは プレーンなReact NativeとExpo(39.x以降)で動作します。

  1. react-native-get-random-valuesのドキュメントを確認し、インストールします。
  2. Nano IDの前にインポートします。
import 'react-native-get-random-values'
import { nanoid } from 'nanoid'

PouchDBとCouchDB

PouchDBとCouchDBでは、IDはアンダースコア_で始めることができません。 Nano IDはデフォルトでIDの先頭に_を使用する可能性があるため、 この問題を防ぐためにプレフィックスが必要です。

次のオプションでデフォルトのIDを上書きします:

db.put({
  _id: 'id' + nanoid(),})

CLI

ターミナルでnpx nanoidを呼び出すことで、一意のIDを取得できます。 システムにNode.jsがあれば十分で、Nano IDをどこかにインストールする必要はありません。

$ npx nanoid
npx: installed 1 in 0.63s
LZfXLFzPPR4NNrgjlWDxn

生成されるIDのサイズは--size(または-s)オプションで指定できます:

$ npx nanoid --size 10
L3til0JS4z

カスタムアルファベットは--alphabet(または-a)オプションで指定できます (この場合、--sizeが必須であることに注意してください):

$ npx nanoid --alphabet abc --size 15
bccbcabaabaccab

TypeScript

Nano IDでは、生成された文字列をTypeScriptで不透明な文字列(opaque strings)にキャストできます。 例えば:

declare const userIdBrand: unique symbol
type UserId = string & { [userIdBrand]: true }

// 明示的な型パラメータを使用:
mockUser(nanoid<UserId>())

interface User {
  id: UserId
  name: string
}

const user: User = {
  // 自動的にUserIdにキャストされます:
  id: nanoid(),
  name: 'Alice'
}

その他のプログラミング言語

Nano IDは多くの言語に移植されています。これらのポートを使用して、 クライアント側とサーバー側で同じID生成器を持つことができます。

その他の環境では、コマンドラインからIDを生成するためのCLIが利用可能です。

ツール

  • IDサイズ計算機は、IDのアルファベットやサイズを調整する際の衝突確率を表示します。
  • nanoid-dictionarycustomAlphabetで使用する一般的なアルファベットを提供します。
  • nanoid-goodはIDに不適切な単語が含まれていないことを確認します。