Skip to main content

@omnitron-dev/cuid

pnpm add @omnitron-dev/cuid

Fast, collision-resistant unique identifier generator. URL-safe, monotonically sortable, suitable for distributed systems where coordination-free ID generation is required.

Verified against packages/cuid/src/.

API

import { cuid } from '@omnitron-dev/cuid';

const id = cuid(); // e.g. 'clh3k7v0a0000q9zoo3y3a8oz'

That's the whole surface. One function, one return type.

Properties

PropertyValue
Length25 characters
Alphabet[a-z0-9] (URL-safe, case-insensitive)
Time-prefixedYes — IDs sort approximately by creation time
Collision-resistant10^15+ per host per millisecond before practical collision
PredictableNo — middle bytes are random
Speed~3M IDs/sec on modern CPUs

Format

c lh3k7v0a 000a q9zoo3y3a8oz
↑ ↑ ↑ ↑
prefix timestamp counter random
(version) (base36) (per-process entropy)

The leading c identifies the format (cuid v1); timestamp + counter give monotonic sort within a host; the random tail guarantees collision-resistance across hosts.

When to use cuid

  • Distributed primary keys — generate on the client, ship to server, no coordination round-trip.
  • Public-facing IDs — short enough for URLs, opaque enough not to leak ordering or scale info.
  • Time-orderable lists — sortable by ID gives approximately-chronological order without an extra timestamp column.

When NOT to use cuid

  • Cryptographic randomness — cuid is collision-resistant but not cryptographically random. For session tokens / API keys use crypto.randomBytes.
  • Sequential keys for analytics — sort order is per-process
    • per-millisecond. Multi-host inserts can re-order.
  • Numeric IDs — cuid is a string. If you need integer primary keys, use a snowflake-style scheme.
  • Sub-millisecond uniqueness — within one process, one millisecond, you get the counter. Below ~10^7 IDs/sec/process, no collisions. Above — extremely unlikely but theoretically possible.

Examples

Generate IDs for entities

import { cuid } from '@omnitron-dev/cuid';

interface User {
id: string;
email: string;
// ...
}

const user: User = {
id: cuid(),
email: 'a@b.c',
};

Request IDs

const requestId = cuid();
logger.info({ requestId }, 'incoming request');

Idempotency keys

async function chargeOnce(amount: number) {
const idempotencyKey = cuid();
return paymentProvider.charge({ amount, idempotencyKey });
}

The client generates a key, retries on network error using the same key — server deduplicates.

Database default

// titan-database / Kysera repository
class UsersTable {
id: Generated<string>; // defaults via cuid()
email: string;
createdAt: Generated<Date>;
}

Most repositories default ID columns to cuid; you almost never generate them manually for inserts.

Performance

ScenarioThroughput
Single-thread modern CPU~3M IDs/sec
Within hot loop (no I/O)~5M+ IDs/sec
Cross-process (per-process counter resets)linear, no contention

The generator is stateless from the caller's view — no lock contention across calls.

Where it's used in the stack

  • titan-database — default for repository primary keys.
  • Omnitron — internal request IDs, process IDs, run IDs.
  • titan-pm — worker pool IDs.
  • titan-events — message IDs.

See also

  • common — sibling utility (predicates, promises, …)
  • titan-database — primary use case for cuid
  • crypto.randomUUID — cryptographic alternative when collision-resistance isn't enough