How to Generate and Validate UUIDs in JavaScript, Python, and Java
You're building an API endpoint that creates a new resource. Your database schema has a UUID primary key. You need to generate that UUID in your application code, not in the database. What's the right way to do it in your stack?
The answer seems obvious -- every language has a UUID library. But the details matter: which version to generate, whether to use the native API or a third-party library, how to validate input that claims to be a UUID, and how not to do any of this.
I've reviewed pull requests where developers used Math.random() to build UUIDs by hand, stored them as TEXT columns, and validated them with regex patterns that accepted invalid values. This guide covers the correct patterns in JavaScript, Python, and Java, with the edge cases that trip people up.
Which UUID Version to Generate
Before writing code, decide which version you need:
| Version | Use Case | Recommendation |
|---|---|---|
| UUID v4 | General-purpose, session tokens, IDs that don't need ordering | Default for most cases |
| UUID v7 | Database primary keys, time-ordered IDs | New default for DB-heavy apps |
| UUID v1 | Legacy systems, time-based with hardware info | Avoid for new systems (privacy concern) |
For a detailed comparison of v4 vs v7, read UUID v4 vs v7. For this guide, I'll show both v4 and v7 generation where supported.
Generating UUIDs in JavaScript
Using crypto.randomUUID() (v4, Native)
Modern JavaScript has a built-in UUID v4 generator. No dependencies, no polyfills:
const id = crypto.randomUUID();
console.log(id);
// => "2d931510-d99f-494a-8c67-87feb05e1594"
Supported in:
- Node.js 19.0+
- Chrome 92+
- Firefox 95+
- Safari 15.4+
- Deno, Bun, and all major edge runtimes (Cloudflare Workers, Vercel Edge)
This should be your default for UUID v4 in JavaScript. It uses the system's CSPRNG (cryptographically secure pseudorandom number generator), same source as crypto.getRandomValues() and TLS key generation.
Important: crypto.randomUUID() only generates v4. There's no parameter to request v7. For v7, use the uuid package.
Using the uuid Package (v4 and v7)
For UUID v7 support or environments where crypto.randomUUID() isn't available:
npm install uuid
import { v4, v7, validate, version } from 'uuid';
// UUID v4
const id4 = v4();
console.log(id4); // => "2d931510-d99f-494a-8c67-87feb05e1594"
// UUID v7
const id7 = v7();
console.log(id7); // => "01911d4b-4c3f-7c21-ae6b-d0a7f72e93f4"
The uuid package is the de facto standard with 2B+ weekly downloads. It's maintained, well-tested, and includes validation, version detection, and format conversion utilities.
Never Do This
// BROKEN: Math.random() is not cryptographically secure
function badUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = Math.random() * 16 | 0;
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
}
Math.random() uses a PRNG with limited state and is predictable after observing enough outputs. Under concurrent load (multiple workers generating IDs simultaneously), collision probability increases significantly compared to a CSPRNG. This pattern shows up in tutorials and legacy code. Don't use it in production.
Validating UUIDs in JavaScript
Regex Approach (Fast, Good for Format Checking)
function isValidUUID(str) {
const pattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
return pattern.test(str);
}
isValidUUID('550e8400-e29b-41d4-a716-446655440000'); // true
isValidUUID('not-a-uuid'); // false
isValidUUID('GGGe8400-e29b-41d4-a716-446655440000'); // false (invalid hex)
This regex checks:
- Correct length (36 chars with hyphens).
- Valid hex characters only (
0-9,a-f). - Version nibble (position 13) is
1through8(covers v1-v8). - Variant nibble (position 14) is
8,9,a, orb(RFC-compliant).
Library-Based Validation (More Robust)
For edge cases and version-specific validation, use the uuid package:
import { validate, version } from 'uuid';
const input = '550e8400-e29b-41d4-a716-446655440000';
if (validate(input)) {
console.log(`Valid UUID v${version(input)}`); // "Valid UUID v4"
}
The library-based approach handles variant parsing and version detection that a simple regex might miss. For production validation of user input, it's the safer choice.
Validating UUIDs in Bulk
If you're processing a CSV or API response with claimed UUIDs:
import { validate } from 'uuid';
const ids = [
'550e8400-e29b-41d4-a716-446655440000', // valid v4
'01911d4b-4c3f-7c21-ae6b-d0a7f72e93f4', // valid v7
'not-a-uuid', // invalid
'GGGe8400-e29b-41d4-a716-446655440000', // invalid hex
];
const results = ids.map(id => ({ id, valid: validate(id) }));
// Filter invalid IDs for error reporting
const invalid = results.filter(r => !r.valid);
For quick ad-hoc validation during development, the UUID generator's validator inspects version, variant, and formatting in one step.
Generating UUIDs in Python
UUID v4 (Built-in)
Python's standard library includes the uuid module. No installation needed:
import uuid
user_id = uuid.uuid4()
print(user_id)
# => UUID('2d931510-d99f-494a-8c67-87feb05e1594')
# As a string (for APIs, database params, JSON serialization)
user_id_str = str(uuid.uuid4())
print(user_id_str)
# => '2d931510-d99f-494a-8c67-87feb05e1594'
uuid.uuid4() uses os.urandom() internally, which is a CSPRNG on all modern platforms.
UUID v7 (via uuid6 Package)
Python's standard library doesn't yet include UUID v7. Use uuid6:
pip install uuid6
from uuid6 import uuid7
id_v7 = uuid7()
print(id_v7)
# => UUID('01911d4b-4c3f-7c21-ae6b-d0a7f72e93f4')
# Convert to string or hex as needed
print(str(id_v7)) # '01911d4b-4c3f-7c21-ae6b-d0a7f72e93f4'
print(id_v7.hex) # '01911d4b4c3f7c21ae6bd0a7f72e93f4'
UUID v5 (Deterministic, Namespace-Based)
When you need the same UUID for the same input every time:
import uuid
# DNS namespace + a unique name = deterministic UUID
user_id = uuid.uuid5(uuid.NAMESPACE_DNS, 'alice@example.com')
print(user_id)
# Always produces the same UUID for 'alice@example.com'
UUID v5 uses SHA-1 hashing, making it suitable for deterministic ID generation from email addresses, URLs, or other stable identifiers. UUID v3 uses MD5 and should be avoided (v5 is strictly better).
Validating UUIDs in Python
Parser-Based Validation (Recommended)
The most reliable approach -- let Python's UUID parser do the work:
import uuid
def is_valid_uuid(value: str) -> bool:
try:
uuid.UUID(value)
return True
except (ValueError, AttributeError):
return False
# Usage
print(is_valid_uuid('550e8400-e29b-41d4-a716-446655440000')) # True
print(is_valid_uuid('not-a-uuid')) # False
print(is_valid_uuid('')) # False
print(is_valid_uuid(None)) # False (ValueError)
The uuid.UUID() constructor validates:
- Correct hex character set.
- Correct segment lengths (8-4-4-4-12).
- Valid version and variant bits.
This is more robust than regex because it catches bit-level violations that pass format checks. It also handles UUIDs with or without hyphens, uppercase and lowercase hex.
Version-Specific Validation
import uuid
def is_valid_uuid_v4(value: str) -> bool:
try:
u = uuid.UUID(value)
return u.version == 4
except (ValueError, AttributeError):
return False
def is_valid_uuid_v7(value: str) -> bool:
try:
u = uuid.UUID(value)
return u.version == 7
except (ValueError, AttributeError):
return False
Converting Between Formats
import uuid
u = uuid.UUID('550e8400-e29b-41d4-a716-446655440000')
print(str(u)) # '550e8400-e29b-41d4-a716-446655440000' (with dashes)
print(u.hex) # '550e8400e29b41d4a716446655440000' (without dashes)
print(u.urn) # 'urn:uuid:550e8400-e29b-41d4-a716-446655440000'
print(str(u).upper()) # '550E8400-E29B-41D4-A716-446655440000' (uppercase)
print(u.bytes) # b'U\x0e\x84\x00\xe2\x9bA\xd4\xa7\x16DfUD\x00\x00'
Generating UUIDs in Java
UUID v4 (Native)
Java has built-in UUID support since JDK 1.5:
import java.util.UUID;
public class Main {
public static void main(String[] args) {
UUID id = UUID.randomUUID();
System.out.println(id);
// => 2d931510-d99f-494a-8c67-87feb05e1594
}
}
UUID.randomUUID() generates v4 using SecureRandom (CSPRNG). It's thread-safe and suitable for production use without additional dependencies.
UUID v7 (Third-Party Libraries)
Java's standard library doesn't yet include v7. Use one of these:
java-uuid-generator (FasterXML):
import com.fasterxml.uuid.Generators;
import java.util.UUID;
UUID id = Generators.timeBasedEpochGenerator().generate();
// Uses UUID v7 encoding, time-ordered
uuid-creator:
import com.github.f4b6a3.uuid.UuidCreator;
import java.util.UUID;
UUID id = UuidCreator.getTimeOrderedEpoch();
// UUID v7 compliant
Converting Between String and UUID
// String to UUID
UUID id = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
// UUID to String
String idStr = id.toString();
// => "550e8400-e29b-41d4-a716-446655440000"
// Without dashes
String noDashes = idStr.replace("-", "");
// => "550e8400e29b41d4a716446655440000"
Validating UUIDs in Java
Parser-Based Validation
import java.util.UUID;
public static boolean isValidUUID(String value) {
try {
UUID.fromString(value);
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
// Usage
System.out.println(isValidUUID("550e8400-e29b-41d4-a716-446655440000")); // true
System.out.println(isValidUUID("not-a-uuid")); // false
System.out.println(isValidUUID(null)); // false (NullPointerException)
Handling Null Input
The fromString method throws NullPointerException on null input, not IllegalArgumentException:
public static boolean isValidUUID(String value) {
if (value == null) return false;
try {
UUID.fromString(value);
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
Version Checking
import java.util.UUID;
public static boolean isUUIDv4(String value) {
try {
UUID uuid = UUID.fromString(value);
return uuid.version() == 4;
} catch (IllegalArgumentException e) {
return false;
}
}
The UUID.version() method (available since Java 9) returns the version number from the UUID's variant bits. Use it to distinguish v4 from v7 from v1.
Cross-Language UUID Patterns
Generating IDs at the Application Layer
The recommended pattern for modern applications is to generate UUIDs in application code, not in the database:
// JavaScript / Node.js
const order = {
id: crypto.randomUUID(),
customerId: req.body.customerId,
amount: req.body.amount
};
await db.insert('orders', order);
# Python
order = {
'id': str(uuid.uuid4()),
'customer_id': customer_id,
'amount': amount
}
await db.execute("INSERT INTO orders (id, customer_id, amount) VALUES ($1, $2, $3)",
order['id'], order['customer_id'], order['amount'])
// Java
UUID orderId = UUID.randomUUID();
PreparedStatement stmt = conn.prepareStatement(
"INSERT INTO orders (id, customer_id, amount) VALUES (?, ?, ?)"
);
stmt.setObject(1, orderId);
stmt.setString(2, customerId);
stmt.setBigDecimal(3, amount);
stmt.executeUpdate();
Generating at the application layer means:
- No database round-trip just to get an ID.
- The ID is available before the INSERT (for logging, event publishing, API response).
- Works identically across sharded databases and read replicas.
UUIDs in JSON APIs
When embedding UUIDs in JSON responses, always use the string format:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Alice"
}
Don't serialize UUIDs as binary in JSON. Every consumer expects the 36-character string format, and it's what libraries in every language parse by default. If you're debugging API responses, the JSON formatter helps inspect UUID-containing payloads clearly.
UUIDs with JWT Tokens
UUIDs frequently appear as JWT claim values (user ID, session ID, request ID). The JWT decoder shows how UUIDs are embedded in token payloads -- useful for debugging auth issues without writing code.
Common Mistakes Across All Languages
Mistake 1: Weak Randomness
Using non-cryptographic PRNGs (Math.random(), Python's random module, Java's java.util.Random):
# WRONG
import random
'%04x' % random.getrandbits(128) # NOT a valid UUID, and NOT unique
# RIGHT
import uuid
uuid.uuid4()
Mistake 2: Storing UUIDs as Text
-- WRONG: 36 bytes per ID, slower comparisons, no type safety
id TEXT PRIMARY KEY
-- RIGHT: 16 bytes per ID, native type, proper indexing
id UUID PRIMARY KEY -- PostgreSQL
id BINARY(16) PRIMARY KEY -- MySQL
The TEXT representation wastes storage, slows down index comparisons (string collation vs binary comparison), and loses type-level validation that prevents inserting non-UUID values into the column.
Mistake 3: Treating UUIDs as Security Tokens
UUIDs identify resources. They don't authenticate users. A UUID-based password reset token or session token is not inherently secure:
// WRONG: UUIDs are not auth tokens
const resetToken = crypto.randomUUID();
// RIGHT: Use a dedicated CSPRNG token
const crypto = require('crypto');
const resetToken = crypto.randomBytes(32).toString('hex');
For cryptographically secure random tokens, use a password generator configured with appropriate length and character set.
Mistake 4: Regex Validation That's Too Permissive
import re
# WRONG: accepts invalid UUIDs
re.match(r'^[a-f0-9-]+$', value)
# RIGHT: validates structure and version/variant bits
import uuid
uuid.UUID(value)
FAQ
What's the simplest way to generate a UUID in JavaScript?
crypto.randomUUID()
No dependencies, no imports, supported in all modern environments. Generates UUID v4.
How do I generate UUID v7 in Python?
pip install uuid6
from uuid6 import uuid7
id = uuid7()
Should I generate UUIDs in the database or in application code?
Generate at the application layer. This avoids a database round-trip, makes the UUID available before INSERT (for logging or publishing events), and works identically across sharded or replicated databases.
Is regex good enough for UUID validation?
For format checking only, yes. A properly written regex catches invalid characters, wrong length, and incorrect segment structure. For bit-level validation (version and variant bits), use a UUID parser library. For production input validation, prefer the parser approach.
How do I store UUIDs efficiently in MySQL?
Use BINARY(16) with UUID_TO_BIN() on insert and BIN_TO_UUID() on select. This stores the UUID in 16 bytes instead of 36 bytes for CHAR(36) or variable-length VARCHAR(36). Combine with UUID v7 for sequential insert ordering that InnoDB's clustered index prefers.
What's the difference between uuid.uuid4() and uuid.uuid7()?
uuid4() generates purely random UUIDs (122 random bits). uuid7() generates time-ordered UUIDs (48-bit timestamp + 74 random bits). v7 values sort chronologically, making them better for database primary keys. v4 values are maximally random, making them better when creation time should not be derivable.
Can I generate UUIDs offline without any library?
Yes, in modern JavaScript: crypto.randomUUID() is built into the runtime. In Python: uuid.uuid4() is in the standard library. In Java: UUID.randomUUID() is in java.util. For UUID v7, you'll currently need a library in all three languages.
If you're integrating UUID generation into your stack and want to test output formats before writing code, the UUID generator supports v1, v4, and v7 with configurable formatting -- dashes/no dashes, uppercase/lowercase, JSON arrays, SQL IN format. Generate bulk batches, validate existing UUIDs, and inspect version and variant information, all running locally in your browser.