DEV Community

EvvyTools
EvvyTools

Posted on

How to Generate and Use UUIDs in JavaScript, Python, and SQL

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'
Enter fullscreen mode Exit fullscreen mode

For Node.js versions before 19, import from the crypto module:

const { randomUUID } = require('crypto');
const id = randomUUID();
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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())
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

For production code, wrap the validation in your request parsing layer rather than scattered throughout business logic.

Server infrastructure with rack-mounted equipment
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');
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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
);
Enter fullscreen mode Exit fullscreen mode

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)