UUID generation is a one-liner in most languages. The interesting questions are which version to generate, where to validate, and how to store and query UUIDs efficiently. This guide walks through the practical details for the three environments where UUID generation comes up most often.
Prerequisites
You should understand the basic structure of a UUID: 32 hexadecimal characters in a 8-4-4-4-12 pattern, with the version encoded in the first character of the third group. If you want the version background before diving into code, the UUID Generator guide on EvvyTools covers v1, v4, and v5 with the full rationale for each.
JavaScript and Node.js
Generating v4 UUIDs
The cleanest approach in modern environments is the native Web Crypto API:
// In browsers and Node.js 19+
const id = crypto.randomUUID();
console.log(id); // e.g., '110e8400-e29b-41d4-a716-446655440000'
For Node.js versions before 19, import from the crypto module:
const { randomUUID } = require('crypto');
const id = randomUUID();
For cross-environment compatibility or if you need v1 or v5 generation as well, the uuid npm package is the standard choice:
import { v4 as uuidv4, v5 as uuidv5 } from 'uuid';
// v4 random
const randomId = uuidv4();
// v5 deterministic
const NAMESPACE = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
const deterministicId = uuidv5('user@example.com', NAMESPACE);
Validating UUIDs in JavaScript
Before using a UUID from an external source, validate it. A simple approach using a regex:
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
function isValidUUID(value) {
return UUID_REGEX.test(value);
}
The uuid package also exports a validate function for convenience:
import { validate as uuidValidate, version as uuidVersion } from 'uuid';
uuidValidate('not-a-uuid'); // false
uuidValidate('110e8400-e29b-41d4-a716-446655440000'); // true
uuidVersion('110e8400-e29b-41d4-a716-446655440000'); // 4
Node.js documentation includes the crypto module reference for the native UUID generation method.
Python
Generating UUIDs
Python's standard library includes a uuid module that covers all three common versions:
import uuid
# v4 random
random_id = str(uuid.uuid4())
print(random_id) # e.g., 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
# v5 deterministic using the DNS namespace
dns_namespace = uuid.NAMESPACE_DNS
deterministic_id = str(uuid.uuid5(dns_namespace, 'example.com'))
# Always returns '9073926b-929f-31c2-abc9-fad77ae3e8eb' for this input
# v1 timestamp-based (includes MAC address)
timestamp_id = str(uuid.uuid1())
Validating UUIDs in Python
The UUID constructor raises a ValueError for invalid inputs:
import uuid
def is_valid_uuid(value):
try:
uuid.UUID(str(value))
return True
except ValueError:
return False
# Check the version
def get_uuid_version(value):
return uuid.UUID(str(value)).version
For production code, wrap the validation in your request parsing layer rather than scattered throughout business logic.

Photo by panumas nikhomkhai on Pexels
SQL: PostgreSQL and MySQL
PostgreSQL
PostgreSQL has native UUID type support. For generation, the pgcrypto extension (which ships with most managed PostgreSQL deployments) provides gen_random_uuid():
-- Enable the extension once per database
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- Generate a v4 UUID inline
SELECT gen_random_uuid();
-- Use as a default for a primary key
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT NOW()
);
-- Insert without specifying the ID
INSERT INTO users (email) VALUES ('user@example.com');
For validating a UUID string in a query, cast it to the UUID type and catch the exception:
-- This will raise an error if the string is not a valid UUID
SELECT '110e8400-e29b-41d4-a716-446655440000'::uuid;
-- Safe version using a try-catch in a function
CREATE OR REPLACE FUNCTION is_valid_uuid(p_uuid text)
RETURNS BOOLEAN AS $$
BEGIN
RETURN (p_uuid::uuid IS NOT NULL);
EXCEPTION WHEN invalid_text_representation THEN
RETURN FALSE;
END;
$$ LANGUAGE plpgsql;
PostgreSQL documentation covers the UUID type and available generation functions in detail.
MySQL
MySQL provides a UUID() function that generates a v1 UUID:
-- Generates a v1 UUID string
SELECT UUID();
-- Strip hyphens if needed
SELECT REPLACE(UUID(), '-', '');
-- Use as a default value (MySQL 8.0+)
CREATE TABLE users (
id VARCHAR(36) PRIMARY KEY DEFAULT (UUID()),
email VARCHAR(255) NOT NULL
);
Note that MySQL's UUID() function generates v1 UUIDs, which embed a timestamp and the server's network hardware identifier. If you need v4 UUIDs in MySQL without writing a custom function, generate them in your application layer and pass the value in the INSERT statement.
Storage and Indexing Notes
When storing UUIDs, prefer the native UUID type in PostgreSQL rather than a VARCHAR column. The native type uses 16 bytes of storage instead of 36 bytes for the string, validates on insert, and is faster to index and compare.
In MySQL and databases without a native UUID type, a binary(16) column is more efficient than VARCHAR(36). Your application layer handles the formatting conversion. The downside is that binary columns are less convenient to inspect directly in a SQL client and require explicit casting in queries. The efficiency tradeoff is usually worth it only for very large tables.
For high-write tables where UUID v4 primary key fragmentation is a documented concern, one pragmatic approach is to use the UUID as an externally-visible identifier while using a serial or bigserial integer as the actual primary key. The integer keeps inserts sequential; the UUID keeps the external identifier opaque. This adds a join column but is a reasonable tradeoff at scale.
Common Mistakes to Avoid
The most frequent UUID-related bugs in production code fall into a few categories.
Storing UUIDs as strings in the wrong type. Using VARCHAR(36) instead of PostgreSQL's native UUID type means every UUID comparison involves a string comparison rather than a 16-byte value comparison. On large indexed tables this is measurably slower and uses more storage.
Skipping validation at API boundaries. Any UUID that arrives from outside your system should be validated before use. An invalid UUID string passed directly to a database query will produce a database error at best and unexpected behavior at worst. Build validation into your request parsing layer, not scattered across individual handlers.
Mixing v1 and v4 in the same foreign key column. If a column is declared as a foreign key to a table that uses v4 UUIDs, inserting a v1 UUID will either cause a foreign key violation (if the value does not exist) or silently store an identifier of a different format. Decide on a version per identifier type and enforce it consistently.
Generating UUIDs with a weak random source. This is rare in modern environments but still appears in legacy code. UUID generation functions that wrap Math.random() or a non-cryptographic pseudo-random generator produce identifiers that are shorter on entropy than they appear. Use crypto.randomUUID(), the uuid package with its cryptographic backend, or the equivalent standard library function in your language.
For quick UUID generation, validation, and bulk generation without any code, the UUID tool at EvvyTools handles all three UUID versions in-browser.
Summary
UUID generation is simple in all three environments. The practical work is in validation at boundaries, choosing the right storage type, and knowing when v4 versus v5 is appropriate. Using native UUID types where the database supports them, building validation into request parsing layers, and documenting which UUID version each identifier type uses are the three practices that prevent the most common UUID-related bugs in production codebases. The guide to UUID versions and when to use each has more depth on the version selection logic.
Top comments (0)