Skip to content

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

Terminal window
npm install @cqrs-ddd/uuidv7
# or
pnpm add @cqrs-ddd/uuidv7

Requires 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); // true
isUuidV7(` ${id}\n`); // true: surrounding whitespace is ignored
isUuidV7('00000000-0000-4000-8000-000000000000'); // false: version 4
isUuidV7(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

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.
  • isUuidV7 checks the textual form, the version and the variant. It does not check that the timestamp is plausible.

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.

Dual-licensed under AGPLv3 and a Commercial License. See the root LICENSE and COMMERCIAL_LICENSE.txt for details.