Every database record, API request, and distributed system needs unique identifiers. UUIDs (Universally Unique Identifiers) solve this elegantly—128-bit values that can be generated anywhere, anytime, with near-zero collision risk. This guide explains what UUIDs are, the differences between versions, and when to use them.
Key Takeaways
- 1UUID is a 128-bit identifier: 32 hex chars in 8-4-4-4-12 format with ~340 undecillion possible values
- 2UUID v4 (random) is most common; UUID v7 (time-sortable) is best for database primary keys
- 3Collision probability is negligible: 2.71 quintillion UUIDs needed for 50% collision chance
- 4Store as BINARY(16) in MySQL, native UUID in PostgreSQL for efficiency
- 5UUIDs prevent enumeration but aren't security tokens—always validate access permissions
1What Is a UUID?
550e8400-e29b-41d4-a716-446655440000
^^^^^^^^ ^^^^ ^^^^ ^^^^ ^^^^^^^^^^^^
| | | | |
time-low | version| node (MAC or random)
time-mid variant| Property | Value |
|---|---|
| Size | 128 bits (16 bytes) |
| Hex characters | 32 |
| Total characters (with hyphens) | 36 |
| Possible values | 2^128 ≈ 340 undecillion |
| Collision probability | Negligible (see below) |
2UUID Versions Explained
| Version | Based On | Best For |
|---|---|---|
| v1 | Timestamp + MAC address | Legacy systems, time ordering |
| v2 | DCE Security (POSIX UIDs) | Rarely used |
| v3 | MD5 hash of namespace + name | Deterministic IDs from names |
| v4 | Random numbers | General purpose (most common) |
| v5 | SHA-1 hash of namespace + name | Deterministic IDs (preferred over v3) |
| v6 | Reordered timestamp + random | Time-ordered, sortable |
| v7 | Unix timestamp + random | Modern sortable (recommended) |
| v8 | Custom/experimental | Vendor-specific implementations |
3UUID v4: The Random Standard
// Generate UUID v4 in JavaScript (native)
const uuid = crypto.randomUUID();
console.log(uuid); // e.g., "f47ac10b-58cc-4372-a567-0e02b2c3d479"
// Using uuid package (Node.js)
import { v4 as uuidv4 } from 'uuid';
const id = uuidv4();
// Python
import uuid
my_uuid = uuid.uuid4()
// Java
UUID uuid = UUID.randomUUID();Generate UUIDs Instantly
Create UUID v4 identifiers with one click. Copy, generate in bulk, or customize format.
Open UUID Generator4UUID v7: The Modern Choice
UUID v7 Structure:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| unix_ts_ms (32 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| unix_ts_ms (16 bits) | ver | rand_a (12 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|var| rand_b (62 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| rand_b |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Advantage | Explanation |
|---|---|
| Time-sortable | UUIDs created later sort after earlier ones |
| Index-friendly | Sequential inserts improve B-tree performance |
| Timestamp extraction | Can extract creation time from UUID |
| Still unique | 74 random bits prevent collisions |
| Drop-in replacement | Same format, works with existing UUID columns |
// Generate UUID v7 (using uuid package v9+)
import { v7 as uuidv7 } from 'uuid';
const id = uuidv7();
// Result: 018f6d3c-e1a2-7xxx-xxxx-xxxxxxxxxxxx
// ^^^^^^^^^^^^
// Timestamp portion (sortable)5UUIDs in Databases
| Aspect | UUID | Auto-Increment INT |
|---|---|---|
| Size | 16 bytes | 4-8 bytes |
| Uniqueness scope | Global (cross-database) | Table only |
| Predictability | Unpredictable (good for security) | Sequential (predictable) |
| Generation | Client or server | Database only |
| Index performance | Random inserts (slower with v4) | Sequential (fast) |
| Merge conflicts | None | Possible |
| URL exposure | Safe (unguessable) | Risky (enumerable) |
-- PostgreSQL native UUID type
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) NOT NULL
);
-- MySQL with BINARY(16) storage (efficient)
CREATE TABLE users (
id BINARY(16) PRIMARY KEY,
email VARCHAR(255) NOT NULL
);
-- Insert with UUID_TO_BIN for MySQL 8+
INSERT INTO users (id, email)
VALUES (UUID_TO_BIN(UUID()), 'user@example.com');6UUID Security Considerations
- UUID v1 leaks MAC address and timestamp—avoid for sensitive data
- UUID v4 is unpredictable but not a security token—always validate access
- UUIDs prevent enumeration attacks (can't guess /users/2 from /users/1)
- Never use UUID as the only access control—check permissions server-side
- UUID v7 reveals creation time—may be sensitive in some contexts
Scenario
Exposing user documents via UUID in URL
Solution
GET /documents/:uuid requires authentication. Server checks if authenticated user owns or has access to that document. UUID prevents guessing, auth prevents unauthorized access.
7When to Use UUIDs (And When Not To)
| Use Case | Recommendation |
|---|---|
| Distributed systems | ✅ UUID (no coordination needed) |
| Public API resource IDs | ✅ UUID (prevents enumeration) |
| Database sharding/replication | ✅ UUID (no conflicts) |
| High-volume logging | ✅ UUID v7 (sortable + unique) |
| Internal sequential data | ⚠️ Consider auto-increment (simpler, smaller) |
| URL shorteners | ❌ Use short IDs (base62, nanoid) |
| Human-readable codes | ❌ Use custom formats (ABC-123) |
| IoT with limited storage | ❌ Use smaller identifiers |
// Alternative: nanoid for shorter IDs
import { nanoid } from 'nanoid';
const id = nanoid(); // "V1StGXR8_Z5jdHi6B-myT" (21 chars)
const short = nanoid(10); // "IRFa-VaY2b" (10 chars)
// For URL-safe, human-readable IDs
// nanoid is 2x faster than UUID and customizable8UUID Best Practices
- Use UUID v4 for general-purpose random IDs
- Use UUID v7 for database primary keys (time-sortable, index-friendly)
- Store as BINARY(16) in MySQL, native UUID type in PostgreSQL
- Generate client-side when possible to reduce server load
- Never rely on UUID alone for access control—always authenticate
- Use lowercase and include hyphens for standard format
- Validate UUID format before database operations to prevent injection
- Consider nanoid or ULID for shorter, URL-friendly identifiers
// Validate UUID format (regex)
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-7][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
function isValidUUID(str) {
return UUID_REGEX.test(str);
}
// Validate with uuid package
import { validate as uuidValidate, version as uuidVersion } from 'uuid';
uuidValidate('not-a-uuid'); // false
uuidValidate('f47ac10b-58cc-4372-a567-0e02b2c3d479'); // true
uuidVersion('f47ac10b-58cc-4372-a567-0e02b2c3d479'); // 4