@cqrs-ddd/uuidv7
Generates and validates UUID version 7 identifiers as defined by
RFC 9562. It has no runtime dependencies and
no framework: it uses Node’s built-in crypto.randomBytes() only.
UUIDv7 puts a millisecond Unix timestamp in the leading bits, so identifiers created in different milliseconds sort by creation time as plain strings. That makes them a good fit for database primary keys, event identifiers and correlation IDs.
Installation
Section titled “Installation”npm install @cqrs-ddd/uuidv7# orpnpm add @cqrs-ddd/uuidv7Requires Node.js 22.12 or later.
Published as an ES module; a CommonJS application loads it with require(). Coming from
0.3.x, see Upgrading from 0.3.x.
import { isUuidV7, uuidv7 } from '@cqrs-ddd/uuidv7';
const id = uuidv7(); // e.g. '01923456-789a-7c3d-9e4f-0123456789ab'
isUuidV7(id); // trueisUuidV7(` ${id}\n`); // true: surrounding whitespace is ignoredisUuidV7('00000000-0000-4000-8000-000000000000'); // false: version 4isUuidV7(42); // false| Export | Signature | Description |
|---|---|---|
uuidv7 |
() => string |
A new UUIDv7 in the canonical lowercase 8-4-4-4-12 form |
isUuidV7 |
(value: unknown) => value is string |
true for a string that is a UUIDv7 once trimmed, in either case |
Format guarantees
Section titled “Format guarantees”Each identifier is 128 bits, written as 36 lowercase hexadecimal characters and hyphens:
| Bits | Field | Content |
|---|---|---|
| 0–47 | unix_ts_ms |
Date.now(), the Unix time in milliseconds |
| 48–51 | ver |
0111 (version 7) |
| 52–63 | rand_a |
cryptographically random |
| 64–65 | var |
10 (the RFC 9562 variant) |
| 66–127 | rand_b |
cryptographically random |
- Identifiers from different milliseconds sort by creation time when compared as strings.
- Identifiers from the same millisecond differ only in their random bits, so their order is random: this implementation does not add a monotonic counter.
- The timestamp comes from the system clock. If the clock moves backwards, later identifiers sort before earlier ones.
isUuidV7checks the textual form, the version and the variant. It does not check that the timestamp is plausible.
Examples
Section titled “Examples”Validate an incoming identifier before using it, and generate one otherwise:
import { isUuidV7, uuidv7 } from '@cqrs-ddd/uuidv7';
function requestId(header: string | undefined): string { return isUuidV7(header) ? header.trim().toLowerCase() : uuidv7();}isUuidV7 accepts surrounding whitespace and uppercase, so normalize an accepted value
before storing it if you compare identifiers as strings.
Read the creation time back from the first 48 bits:
import { uuidv7 } from '@cqrs-ddd/uuidv7';
const id = uuidv7();const createdAt = new Date(Number.parseInt(id.replace(/-/g, '').slice(0, 12), 16));Migrating from @nestjs-pipeline/core 0.1.x
Section titled “Migrating from @nestjs-pipeline/core 0.1.x”uuidv7 and isUuidV7 were exported by @nestjs-pipeline/core 0.1.x, and uuidv7
also by @nestjs-pipeline/correlation 0.1.x. Neither package exports them in 0.2.0.
// Before (0.1.x)import { isUuidV7, uuidv7 } from '@nestjs-pipeline/core';import { uuidv7 } from '@nestjs-pipeline/correlation';
// After (0.2.0)import { isUuidV7, uuidv7 } from '@cqrs-ddd/uuidv7';The signatures and the output format are unchanged. Add @cqrs-ddd/uuidv7 to your own
dependencies: it is a dependency of the pipeline packages, not a re-export.
License
Section titled “License”Dual-licensed under AGPLv3 and a Commercial License. See the root
LICENSE and
COMMERCIAL_LICENSE.txt
for details.