Node.js Distributed IDs: UUIDv7 vs ULID vs CUID2 (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.

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.
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.
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.

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.
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.
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) });
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.
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.
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.
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.
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.
