NanoID چیست؟
NanoID یک تولیدکننده شناسه تصادفی کوچک، سریع و امن برای URL است. بهطور پیشفرض رشتههای ۲۱ کاراکتری با الفبای ۶۴ کاراکتری (A-Za-z0-9_-) تولید میکند و آنتروپی تقریبی ۱۲۶ بیتی فراهم میکند — قابل مقایسه با ۱۲۲ بیت UUID v4 اما در رشتهای کوتاهتر.
NanoID هیچ زماننما یا داده ساختارمندی را جاسازی نمیکند. هر شناسه کاملاً تصادفی است و از مولد اعداد تصادفی امن رمزنگاریشده سیستمعامل تولید میشود (crypto.getRandomValues() در مرورگرها، crypto.randomBytes() در Node.js).
NanoID در برابر UUID v4
NanoID و UUID v4 هر دو تولیدکننده شناسه تصادفی هستند که از CSPRNG پشتیبانی میشوند. تفاوتهای آنها در قالب، طول و پشتیبانی اکوسیستم است:
| ویژگی | NanoID (پیشفرض) | UUID v4 |
|---|---|---|
| قالب | الفبای عددی امن برای URL + _- | هگزادسیمال با خط تیره |
| طول | ۲۱ کاراکتر (پیشفرض) | ۳۶ کاراکتر |
| آنتروپی | ~۱۲۶ بیت | ۱۲۲ بیت |
| امن برای URL | بله — نیازی به رمزگذاری نیست | بله (با خط تیره) |
| الفبا | ۶۴ کاراکتر (A-Za-z0-9_-) | ۱۶ کاراکتر (0-9a-f) |
| وابستگیها | نیاز به بسته npm دارد | بومی (crypto.randomUUID) |
| قابل تنظیم | بله — طول و الفبا | خیر |
| استاندارد | هیچ (کتابخانه جامعهمحور) | RFC 4122 / RFC 9562 |
UUID v4 را انتخاب کنید وقتی سازگاری با سیستمهای خارجی اهمیت دارد — پایگاههای داده با ستونهای UUID بومی، APIهایی که قالب UUID انتظار دارند، یا زیرساخت ثبت رویداد که UUIDها را تجزیه میکند. NanoID را انتخاب کنید وقتی شناسههای کوتاهتر میخواهید و کنترل کامل پشته را دارید.
احتمال تصادم بر اساس اندازه
احتمال تصادم NanoID به طول شناسه و نرخ تولید بستگی دارد. جدول زیر از الفبای پیشفرض ۶۴ کاراکتری استفاده میکند:
| اندازه (کاراکتر) | شناسههای ممکن | ایمنی در برابر تصادم |
|---|---|---|
| 6 | 64 | ~۱ در ۴.۵ میلیارد — ایمن برای چند هزار شناسه |
| 8 | 64 | ~۱ در ۴.۵ تریلیون — ایمن برای میلیونها شناسه |
| 11 | 64 | ~۱ در ۲.۸ کوادریلیون — ایمن برای میلیاردها شناسه |
| 16 | 64 | ~۱ در ۱.۲ × 10^19 — ایمن برای تریلیونها شناسه |
| 21 | 64 | ~۱ در 10^30 — ایمن برای ۱۰۰ میلیارد شناسه در روز برای قرنها |
| 32 | 64 | قابل مقایسه با UUID v4 (۱۲۲ بیت) |
| 36 | 36 | از UUID v4 فراتر میرود |
اندازه پیشفرض ۲۱ کاراکتر برای تطابق با مقاومت تصادم UUID v4 (~۱۲۶ بیت) و در عین حال ۴۱٪ کوتاهتر بودن انتخاب شده است. برای اکثر برنامهها، ۲۱ کاراکتر انتخاب مناسبی است.
الفباهای سفارشی
الفبای NanoID کاملاً قابل تنظیم است. این کتابخانه هر رشتهای از کاراکترهای یکتا را به عنوان الفبا میپذیرد و شناسهها را فقط با استفاده از آن کاراکترها تولید میکند:
A-Za-z0-9_-A-Za-z0-90-9a-f0-9مهم: از nanoid/non-secure فقط برای برنامههایی استفاده کنید که نیاز به امنیت ندارند (مثلاً شناسههای عناصر رابط کاربری). برای هر شناسهای که باید غیرقابل حدس باشد، همیشه از default import امن استفاده کنید.
نحوه تولید تصادفی بودن در NanoID
NanoID از مولد اعداد شبهتصادفی امن رمزنگاریشده (CSPRNG) سیستمعامل استفاده میکند. در مرورگرها این crypto.getRandomValues() است؛ در Node.js این crypto.randomFillSync() است. این همان منبع آنتروپی است که برای کلیدهای نشست TLS استفاده میشود — بسیار قویتر از Math.random().
نمونهبرداری رد (جلوگیری از انحراف پیمانهای)
یک رویکرد ساده برای تولید کاراکترهای تصادفی این است: یک بایت تصادفی (0 تا 255) بگیرید و byte % alphabetSize را محاسبه کنید. این انحراف پیمانهای ایجاد میکند — برخی کاراکترها کمی بیشتر از دیگران ظاهر میشوند وقتی اندازه الفبا مضربی از 256 نباشد.
NanoID این انحراف را با نمونهبرداری رد از بین میبرد:
- کوچکترین ماسک توان-دو که اندازه الفبا را پوشش میدهد تعیین کنید (مثلاً برای الفبای ۶۴ کاراکتری، ماسک ۶۳ = 0b00111111 است)
- بایتهای تصادفی تولید کنید و ماسک را اعمال کنید:
byte & mask - اگر مقدار ماسکشده در محدوده الفبا بود، از آن استفاده کنید. در غیر این صورت، آن را دور بیندازید و دوباره تلاش کنید.
این یعنی برخی بایتهای تصادفی دور انداخته میشوند، اما نتیجه توزیع کاملاً یکنواخت بر روی الفبا است — هیچ کاراکتری بیشتر از دیگری محتمل نیست.
// Pure browser — no npm package needed
function generateNanoid(alphabet, size) {
const mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1
const step = Math.ceil((1.6 * mask * size) / alphabet.length)
let id = ''
while (id.length < size) {
const bytes = crypto.getRandomValues(new Uint8Array(step))
for (const byte of bytes) {
const idx = byte & mask
if (idx < alphabet.length) {
id += alphabet[idx]
if (id.length === size) break
}
}
}
return id
}
const URL_SAFE = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'
generateNanoid(URL_SAFE, 21) // → "V1StGXR8_Z5jdHi6B-myT"پشتیبانی محیط
نمونه کدها
JavaScript / TypeScript
// npm i nanoid
import { nanoid } from 'nanoid'
nanoid() // → "V1StGXR8_Z5jdHi6B-myT" (21 chars, URL-safe)
nanoid(8) // → "Uakgb_J5" (custom size)
// Custom alphabet
import { customAlphabet } from 'nanoid'
const hexId = customAlphabet('0123456789abcdef', 16)
hexId() // → "4f3a1b8c9d2e0f7a"
const numId = customAlphabet('0123456789', 8)
numId() // → "30812894"مرورگر (CDN)
NanoID را میتوان مستقیماً در مرورگر از طریق import از CDN استفاده کرد. برای نمونهسازی سریع نیازی به مرحله ساخت نیست.
// Pure browser — no npm package needed
function generateNanoid(alphabet, size) {
const mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1
const step = Math.ceil((1.6 * mask * size) / alphabet.length)
let id = ''
while (id.length < size) {
const bytes = crypto.getRandomValues(new Uint8Array(step))
for (const byte of bytes) {
const idx = byte & mask
if (idx < alphabet.length) {
id += alphabet[idx]
if (id.length === size) break
}
}
}
return id
}
const URL_SAFE = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'
generateNanoid(URL_SAFE, 21) // → "V1StGXR8_Z5jdHi6B-myT"Python
# pip install nanoid
from nanoid import generate
generate() # → "V1StGXR8_Z5jdHi6B-myT"
generate(size=8) # → "Uakgb_J5"
generate('0123456789abcdef', 16) # custom alphabet + sizeNode.js (CommonJS)
// Node.js — stdlib only, no npm needed
const { randomFillSync } = require('crypto')
function nanoid(alphabet, size) {
const mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1
const step = Math.ceil((1.6 * mask * size) / alphabet.length)
let id = ''
while (id.length < size) {
const bytes = randomFillSync(Buffer.alloc(step))
for (const byte of bytes) {
const idx = byte & mask
if (idx < alphabet.length) { id += alphabet[idx]; if (id.length === size) break }
}
}
return id
}