How to generate a UUID in PostgreSQL, JavaScript, Python, Java and C#
Updated October 12, 2026 · Reading time: 6 min · 9 sections
Before you add a package to generate a UUID, call the one your database or runtime already ships: every mainstream platform has a secure v4 built in, and most now have v7. The calls are short; the traps sit in the details around them.
PostgreSQL
Use the native uuid type: 16 bytes, printed in lower case, any case accepted on input. gen_random_uuid() is built in, so the usual table looks like this:
CREATE TABLE orders (id uuid PRIMARY KEY DEFAULT gen_random_uuid(), placed_at timestamptz NOT NULL);
PostgreSQL 18 adds uuidv4(), the same thing under a clearer name, and uuidv7() for time-ordered keys. An optional interval shifts the embedded timestamp:
ALTER TABLE orders ALTER COLUMN id SET DEFAULT uuidv7();SELECT uuidv7(interval '-1 hour');
Two functions read a value back: uuid_extract_version(id) returns the version, and uuid_extract_timestamp(id) returns the embedded time for v1 and v7 (NULL for other versions). For name-based IDs, the uuid-ossp extension has v3 and v5:
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";SELECT uuid_generate_v5(uuid_ns_dns(), 'python.org');
That returns 886313e1-3b8a-5372-9b90-0c9aee199e5d, the same value Python gives below. The full list is in the PostgreSQL UUID functions reference.
JavaScript in the browser and Node.js
crypto.randomUUID() returns a 36-character lower-case v4 from a cryptographically secure generator. In browsers it exists only in secure contexts: HTTPS pages and localhost. Open the same page over plain HTTP on a LAN address and the function is undefined, so the call throws. In Node.js it lives on the crypto module:
import { randomUUID } from 'node:crypto';
There is no built-in v7 or v5. If you need another version, start from secure random bytes and set the version and variant bits yourself:
const b = crypto.getRandomValues(new Uint8Array(16));b[6] = (b[6] & 0x0f) | 0x40;b[8] = (b[8] & 0x3f) | 0x80;
For v7 you would also write the 48-bit millisecond timestamp into b[0] to b[5], use 0x70 as the version, and add a counter if IDs from the same millisecond must stay in order. That counter is where hand-rolled v7 code usually goes wrong; the v4 vs v7 guide explains the method.
Math.random(). Old snippets that replace x and y in a template string with Math.random digits are still copied around, and that generator is not cryptographically secure.Python
import uuiduuid.uuid4()uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
Each call returns a UUID object: str() gives the canonical lower-case form, .hex the 32 digits, .bytes the 16 raw bytes. The v5 call returns 886313e1-3b8a-5372-9b90-0c9aee199e5d on every machine, every time; uuid3 with the same inputs gives 6fa459ea-ee8a-3ca4-894e-db77e160355e.
Python 3.14 adds uuid.uuid6(), uuid.uuid7() and uuid.uuid8(); its uuid7 keeps a 42-bit counter so values from one process are monotonic. On earlier versions you need a third-party package for v7. Avoid uuid1(): the Python docs warn it can embed the machine's network address.
Java
UUID id = UUID.randomUUID();UUID parsed = UUID.fromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8");
randomUUID() is a v4 from SecureRandom. The trap is UUID.nameUUIDFromBytes(): it makes a v3 (MD5) from only the bytes you pass, with no namespace. UUID.nameUUIDFromBytes("python.org".getBytes(StandardCharsets.UTF_8)) gives db0fe87d-2a44-35fb-a0f0-6fa792ac61ea, not Python's 6fa459ea-ee8a-3ca4-894e-db77e160355e.
To match RFC v3, pass the 16 bytes of the namespace UUID followed by the UTF-8 bytes of the name; with the DNS namespace and "python.org" you then get 6fa459ea-ee8a-3ca4-894e-db77e160355e. A namespace's bytes come from ByteBuffer.allocate(16).putLong(ns.getMostSignificantBits()).putLong(ns.getLeastSignificantBits()).array(). The long-term-support JDKs have no v5 or v7 factory in java.util.UUID, so use a library or build v7 from SecureRandom bytes the same way as in JavaScript.
C# and .NET
Guid id = Guid.NewGuid();Guid ordered = Guid.CreateVersion7();
NewGuid() gives a random v4. CreateVersion7() exists in .NET 9 and later, with an overload that takes a DateTimeOffset. Formatting is a format letter away: id.ToString() is lower case with hyphens, id.ToString("B") adds braces, id.ToString("N") gives 32 digits with no hyphens. .NET has no built-in v5.
Two habits catch people moving GUIDs out of .NET: ToByteArray() writes the first three groups little-endian, and SQL Server sorts uniqueidentifier by its last group first, which undoes the ordering of a v7. Both are covered in UUID vs GUID.
MySQL and SQL Server
MySQL's UUID() returns a version 1 value built from a timestamp and a node ID, not a random v4. If you store it as BINARY(16), the swap flag reorders the time fields so stored bytes rise with time:
INSERT INTO items (id) VALUES (UUID_TO_BIN(UUID(), 1));SELECT BIN_TO_UUID(id, 1) FROM items;
Use the same flag in both directions, or you will read back scrambled IDs. For v4 or v7 in MySQL, generate in the application and insert with UUID_TO_BIN('...') or a 0x hex literal.
SQL Server's NEWID() returns a random GUID. NEWSEQUENTIALID() is allowed only as a DEFAULT on a uniqueidentifier column; its values increase on one machine and can restart from a lower range after Windows restarts.
Which call to use on each platform
| Platform | v4 | v7 | Name-based (v5 / v3) |
|---|---|---|---|
| PostgreSQL | gen_random_uuid(), uuidv4() (18) | uuidv7() (18) | uuid_generate_v5(), uuid_generate_v3() via uuid-ossp |
| JavaScript | crypto.randomUUID() | Not built in | Not built in |
| Python | uuid.uuid4() | uuid.uuid7() (3.14) | uuid.uuid5(), uuid.uuid3() |
| Java | UUID.randomUUID() | Not in java.util.UUID (LTS releases) | UUID.nameUUIDFromBytes(), v3 only, namespace bytes prepended by you |
| C# / .NET | Guid.NewGuid() | Guid.CreateVersion7() (.NET 9) | Not built in |
| MySQL | Not built in (UUID() is v1) | Not built in | Not built in |
| SQL Server | NEWID() | Not built in | Not built in |
Three things I check in code review
- The column type. A
VARCHAR(36)key costs 36 bytes or more per row in every index that contains it, against 16 foruuid,uniqueidentifierorBINARY(16), and its comparisons depend on collation and case. - UUIDs used as secrets. RFC 9562 §8 says not to assume UUIDs are hard to guess. A password-reset or invite link needs a token from a secure random generator, checked on the server, even if a UUID also identifies the row.
- Unstable names for v5 and v3. A trailing slash, a capital letter or a different namespace gives a different ID. Normalise the name before hashing and write the rule down next to the code.
Test data without writing a loop
For seed data and fixtures you often need no code at all. The Bulk tab of the UUID generator makes 1 to 10,000 IDs per list in any version and downloads them as TXT, CSV with a uuid header, a JSON array, or a ready INSERT INTO items (id) VALUES ('…'), …; statement; rename the table and paste. v7 lists come out strictly ascending. For v5 and v3, enter one name per line and you get the same IDs Python's uuid5 and PostgreSQL's uuid_generate_v5 produce, so fixtures stay reproducible. Everything is generated in the browser with crypto.getRandomValues and nothing is sent to a server.
FAQ
How do I generate a UUID v7 in JavaScript?
There is no built-in function. Use a library, or fill 16 bytes from crypto.getRandomValues, write the millisecond timestamp into the first six, set version 7 and the variant bits, and add a counter if IDs from the same millisecond must stay ordered.
Why is crypto.randomUUID undefined?
The page is not a secure context. Browsers expose it only on HTTPS and localhost, so a page opened over plain HTTP on a network address does not have it.
Why doesn't Java's nameUUIDFromBytes match Python's uuid3?
Java hashes only the bytes you pass. Python hashes the namespace UUID bytes followed by the name. Prepend the 16 namespace bytes in Java and the results match.
Does MySQL UUID() return a version 4 UUID?
No. It returns version 1, built from a timestamp and a node ID. Generate v4 or v7 in your application if you need those versions.