Node.js Distributed IDs: UUIDv7 vs ULID vs CUID2 (2026)
product-development10 min readintermediate

Node.js Distributed IDs: UUIDv7 vs ULID vs CUID2 (2026)

Vivek Singh
Founder & CEO at Witarist · June 13, 2026

For two decades the default primary key in a Node.js backend was a BIGSERIAL auto-increment integer. It was compact, sortable, and index-friendly — but it leaks row counts, requires a round trip to the database before you know the ID, and falls apart the moment you shard or merge data across services. The industry answer was UUIDv4: random, globally unique, generated anywhere. The problem is that random UUIDs are terrible primary keys at scale, shredding your B-tree index and tanking insert throughput.

In 2026 the conversation has shifted decisively toward time-ordered identifiers. UUIDv7 is now a ratified IETF standard (RFC 9562), ULID has a mature ecosystem, and CUID2 and Nano ID cover the cases where you want short, URL-friendly, or collision-tunable IDs. This guide breaks down each option, shows the database performance differences that actually matter, and gives you copy-paste Node.js code to generate them all the right way.

Why Auto-Increment and UUIDv4 Both Break at Scale

The auto-increment ceiling

Auto-increment keys are sequential and cache-friendly, but they couple identity to a single database server. You cannot generate one client-side, you cannot merge two databases without rewriting keys, and exposing them in URLs leaks how many users or orders you have. In a distributed system where multiple services create records independently, a centralized counter becomes a bottleneck and a single point of failure.

The hidden cost of random UUIDv4

UUIDv4 solves uniqueness and decentralization, but its 122 random bits mean every insert lands at a random position in the index. On a clustered index (MySQL InnoDB) or any B-tree primary key, this causes constant page splits, poor cache locality, and write amplification. Teams routinely see 40-60% lower insert throughput and bloated indexes once a UUIDv4 table grows past a few million rows.

⚠️Warning
Random UUIDv4 as a clustered primary key is one of the most common causes of mysterious write-performance degradation in growing Node.js apps. The table looks fine at 100k rows and falls off a cliff at 10M.
Comparison table of Node.js UUIDv7, ULID, CUID2, Nano ID and UUIDv4 ID strategies across size, sortability, URL-safety, collision resistance and database index performance
Figure 1 — How the five common Node.js ID strategies compare across the dimensions that matter for production.

UUIDv7: Time-Ordered UUIDs Done Right

UUIDv7 keeps the familiar 128-bit, hyphenated UUID format that every database, ORM, and library already understands — but it replaces the leading bits with a 48-bit Unix millisecond timestamp followed by random data. The result is an identifier that is globally unique like UUIDv4 yet roughly sortable by creation time like an auto-increment key. Because it is a drop-in for any existing uuid column, migrating is usually a one-line generator change.

What you get for free

Because the timestamp is embedded in the ID, you can sort by primary key to get chronological order, range-scan by time window, and even extract the creation time without a separate created_at column. Index locality is excellent: new rows cluster at the right edge of the B-tree, so page splits and cache misses drop dramatically compared with UUIDv4.

ℹ️Note
UUIDv7 is defined in RFC 9562 (published 2024), which finally replaced the old RFC 4122. Postgres 18 and most modern drivers can store it natively in a uuid column with zero schema changes.
Figure 2 — Interactive scorecard: each ID strategy rated across five production dimensions.

ULID, CUID2 and Nano ID: The Alternatives

ULID — sortable and URL-safe

ULID encodes a 48-bit timestamp plus 80 bits of randomness into a 26-character Crockford base32 string. It is lexicographically sortable like UUIDv7 but, unlike a hyphenated UUID, it is compact and safe to drop into URLs without encoding. If you want time-ordering AND human-shareable identifiers, ULID is often the sweet spot.

CUID2 — collision-safe without leaking time

CUID2 deliberately does NOT embed a timestamp, which makes it the right choice when you do not want an identifier to reveal when a record was created. It is engineered for collision resistance across distributed clients and produces short, URL-safe strings. The trade-off is that you lose free time-ordering, so it is a weaker primary-key choice for high-write tables.

Nano ID — the minimalist token

Nano ID is a tiny, fast generator producing URL-safe random strings with a tunable alphabet and length. It is ideal for opaque tokens, share links, and short codes rather than database primary keys, because it offers no ordering and its collision probability depends entirely on the length you choose.

Ready to build your team?

Hire Pre-Vetted Node.js Developers

Skip the months-long search. Our exclusive talent network has senior Node.js experts ready to join your team in 48 hours.

Horizontal bar chart of Node.js ID generation throughput in operations per second for Nano ID, UUIDv4, UUIDv7, ULID and CUID2
Figure 3 — Raw generation throughput. All options are fast enough that DB behaviour, not generator speed, should drive your choice.

Database Performance: Why ID Ordering Beats ID Speed

Generator microbenchmarks are fun but mostly irrelevant — even the slowest option here produces over a million IDs per second, far more than any API needs. What actually moves your p99 latency is how the ID interacts with the database index. Time-ordered keys append near the rightmost B-tree page, so inserts stay cache-resident and the index stays compact. Random keys scatter writes across the whole tree, forcing page splits, more WAL, and a larger working set.

What the numbers show

In bulk-insert benchmarks on an indexed Postgres table, UUIDv7 and ULID land within ~8% of a plain BIGSERIAL, while random UUIDv4 drops nearly in half. If those numbers matter to your roadmap, it is worth having a PostgreSQL specialist review your indexing strategy before you commit to a key format across hundreds of tables.

Figure 4 — Interactive: bulk INSERT throughput into an indexed Postgres table by key type.

Generating Every ID Type in Node.js

Here is a single reference module that generates all four identifier types the modern way. UUIDv4 ships natively via node:crypto

The snippet below shows the canonical generator for each strategy, plus a small helper that recovers the embedded timestamp from a UUIDv7 — a handy trick for auditing and debugging without an extra column.

ids.js
import { randomUUID } from 'node:crypto';
import { ulid } from 'ulid';
import { createId } from '@paralleldrive/cuid2';
import { uuidv7 } from 'uuidv7';

// 1. UUIDv7 — RFC 9562, time-ordered, drop-in for any uuid column
const orderId = uuidv7();          // '0192f3c1-...' first 48 bits = ms timestamp

// 2. ULID — Crockford base32, lexicographically sortable, URL-safe
const eventId = ulid();            // '01JRT8...' 26 chars

// 3. CUID2 — collision-resistant, no embedded timestamp (privacy-friendly)
const sessionId = createId();      // 'tz4a98x...' ~24 chars

// 4. Native UUIDv4 — random, fine for non-indexed / opaque tokens
const nonce = randomUUID();        // 'f47ac10b-...'

// Helper: extract the timestamp baked into a UUIDv7 for free auditing
function uuidv7ToDate(id) {
  const hex = id.replace(/-/g, '').slice(0, 12); // first 48 bits
  return new Date(parseInt(hex, 16));
}

console.log({ orderId, eventId, sessionId, createdAt: uuidv7ToDate(orderId) });
🚀Pro Tip
Pick ONE primary-key strategy per service and enforce it in a shared package. Mixing UUIDv4 and UUIDv7 across tables makes it impossible to reason about index behaviour and breaks any assumption that sorting by id means sorting by time.

Choosing the Right Strategy for Your Stack

For most new services the default should be UUIDv7: it is standardized, time-ordered, and a zero-friction drop-in for any uuid column. Reach for ULID when you want those same properties but in compact, URL-safe strings. Use CUID2 when you must not leak creation time, and keep Nano ID for opaque tokens and share links. Whatever you choose, the bigger win usually comes from getting the data-access layer right — which is exactly where an experienced Node.js backend developer earns their keep.

If you are migrating an existing UUIDv4 table, you do not need a risky big-bang rewrite. New rows can start using UUIDv7 immediately, and you can backfill or leave historical rows as-is since the formats are byte-compatible in a uuid column. The ordering benefit applies to all future inserts, which is where your growth — and your index pain — actually lives.

If you are designing the data layer for a new Node.js system and want it right the first time, HireNodeJS connects you with pre-vetted senior engineers — people who have shipped UUIDv7 migrations and high-write Postgres schemas in production — available within 48 hours, without the recruiter overhead.

Hire Expert Node.js Developers — Ready in 48 Hours

Choosing the right identifier is one decision among hundreds in a production Node.js system — and you need engineers who get all of them right. HireNodeJS.com specialises exclusively in Node.js talent: every developer is pre-vetted on real-world projects, API design, database performance, and production deployments.

Unlike generalist platforms, our curated pool means you speak only to engineers who live and breathe Node.js. Most clients have their first developer working within 48 hours of getting in touch. Engagements start as short-term contracts and can convert to full-time hires with zero placement fee.

💡Tip
🚀 Ready to scale your Node.js team? HireNodeJS.com connects you with pre-vetted engineers who can join within 48 hours — no lengthy screening, no recruiter fees. Browse developers at hirenodejs.com/hire

Conclusion

The era of defaulting to auto-increment integers or random UUIDv4 keys is over. Time-ordered identifiers give you the decentralization of UUIDs with the index locality of sequential keys, and in 2026 UUIDv7 is the standardized, well-supported default for most Node.js services. ULID, CUID2, and Nano ID round out the toolkit for URL-friendly, privacy-conscious, and token use cases respectively.

Match the identifier to the job, enforce one strategy per service, and remember that for high-write tables the ordering of your keys matters far more than the raw speed of generating them. Get those fundamentals right and your indexes — and your on-call engineers — will thank you.

Topics
#Node.js#UUIDv7#ULID#CUID2#Database Performance#PostgreSQL#Primary Keys#System Design

Frequently Asked Questions

What is the best primary key for a Node.js app in 2026?

For most services, UUIDv7 is the best default. It is a ratified RFC 9562 standard, globally unique, and time-ordered, so it keeps database indexes compact while remaining a drop-in for any existing uuid column.

Is UUIDv7 better than UUIDv4 for database performance?

Yes, significantly. UUIDv7 embeds a timestamp so new rows cluster at the right edge of the B-tree index, avoiding the page splits and write amplification that random UUIDv4 causes. Insert throughput is typically 40-60% higher on large indexed tables.

Should I use UUIDv7 or ULID?

Both are time-sortable. Choose UUIDv7 if you want a standard 128-bit UUID that drops into existing uuid columns. Choose ULID if you prefer a compact 26-character, URL-safe string and do not need the canonical UUID format.

When should I use CUID2 instead of UUIDv7?

Use CUID2 when you specifically do not want the identifier to leak creation time, since CUID2 contains no embedded timestamp. It is also shorter and URL-safe, but it is a weaker choice for high-write primary keys because it is not time-ordered.

Can I migrate from UUIDv4 to UUIDv7 without downtime?

Yes. UUIDv7 and UUIDv4 are byte-compatible in a uuid column, so you can switch your generator to UUIDv7 for all new rows immediately and leave historical rows untouched. No schema change or risky big-bang rewrite is required.

How fast can Node.js generate these IDs?

All options generate well over a million IDs per second on a single thread, so generator speed is rarely the bottleneck. The database index behaviour driven by ordering matters far more than raw generation throughput.

About the Author
Vivek Singh
Founder & CEO at Witarist

Vivek Singh is the founder of Witarist and HireNodeJS.com — a platform connecting companies with pre-vetted Node.js developers. With years of experience scaling engineering teams, Vivek shares insights on hiring, tech talent, and building with Node.js.

Developers available now

Need a Node.js engineer who designs data layers that scale?

HireNodeJS connects you with pre-vetted senior Node.js engineers who have shipped UUIDv7 migrations and high-write Postgres schemas in production — available within 48 hours, no recruiter fees.