crypto.randomUUID()
Generates a cryptographically random RFC 4122 version 4 UUID as a 36-character string.
crypto.randomUUID([options])- Added in Node.js v15.6.0, v14.17.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$optionsoptional | Object | Optional settings object. |
$options.disableEntropyCacheoptional | boolean= false | By default Node.js generates and caches enough random data to produce up to 128 UUIDs at a time. Set to true to bypass the cache and draw fresh entropy for every call. |
Return value
stringA 36-character RFC 4122 version 4 UUID string in the canonical 8-4-4-4-12 hyphenated form.
Errors
Throws ERR_INVALID_ARG_TYPE if options is not an object, or ERR_INVALID_ARG_VALUE if disableEntropyCache is not a boolean.
crypto.randomUUID() is the built-in replacement for the uuid package when all you need is a version 4 identifier. It returns a string like 109156be-c4fb-41ea-b1b4-efe1671c5836: 32 hex digits in five hyphen-separated groups, with 122 bits of randomness drawn from the same CSPRNG that backs crypto.randomBytes().
When to reach for it
Use it for anything that needs an identifier no one can guess or enumerate: database primary keys, idempotency keys on a payment endpoint, request correlation IDs, filenames for uploaded objects. Because the value carries no timestamp and no machine identity, it leaks nothing about when or where it was created.
What trips people up
Version 4 UUIDs are deliberately unordered. Using one as a clustered primary key in MySQL or SQL Server scatters inserts across the whole B-tree and wrecks write throughput on large tables. If you need identifiers that sort by creation time, use a time-ordered scheme instead — Node 24.16.0 and 26.1.0 added crypto.randomUUIDv7() for exactly this.
The other surprise is the entropy cache. Node pre-generates enough randomness for 128 UUIDs to keep the call fast. That is a pure performance optimisation and does not weaken the output, but in a process that only ever needs one or two UUIDs the buffer is wasted memory, which is what disableEntropyCache: true exists to avoid.
Example
When to use it
- Generating an idempotency key so a retried payment request is not charged twice
- Creating a correlation ID attached to every log line for one HTTP request
- Naming an uploaded file in object storage so it cannot collide or be guessed
- Producing primary keys client-side before a row is ever sent to the database
Frequently asked questions
Do I still need the uuid npm package in Node.js?
Not for version 4 UUIDs — crypto.randomUUID() has been built in since Node 14.17.0 and 15.6.0 and produces the same RFC 4122 v4 format. You still need the package if you want v1, v3 or v5 UUIDs, or if you must parse and validate UUIDs, none of which the built-in covers.
Is crypto.randomUUID() safe to use as a security token?
Yes, it is generated by a cryptographic pseudorandom number generator and carries 122 bits of randomness, which is far beyond guessable. It is a reasonable session or capability token, though for a bearer token most people prefer crypto.randomBytes(32).toString('base64url') because it packs more entropy into fewer characters.
Why are my UUID primary keys making the database slow?
Version 4 UUIDs are random, so consecutive inserts land at random points in a clustered index and force constant page splits. Either use a non-clustered UUID column with a separate sequential clustering key, or switch to a time-ordered identifier such as crypto.randomUUIDv7().
More examples
Idempotency keys for a retried request
The key is created outside the retry loop. If it were generated inside, every retry would look like a brand new charge to the server and the customer could be billed three times. The output is just the function type — the request itself needs network access.
import crypto from 'node:crypto';
// Generate the key ONCE, before the first attempt. Reuse it on every retry
// so the server can recognise the duplicate and return the original result.
async function chargeCard(amountCents) {
const idempotencyKey = crypto.randomUUID();
for (let attempt = 1; attempt <= 3; attempt++) {
try {
const res = await fetch('https://api.example.com/charges', {
method: 'POST',
headers: {
'content-type': 'application/json',
'idempotency-key': idempotencyKey,
},
body: JSON.stringify({ amountCents }),
});
if (res.ok) return res.json();
} catch (err) {
if (attempt === 3) throw err;
}
}
}
console.log(typeof chargeCard);Correlation IDs in an Express request logger
Every log line for a single request carries the same id, so a slow or failing request can be traced across services by grepping one value. Echoing it back in the x-request-id response header lets a client quote the id in a bug report. This needs a listening server, so it is shown rather than run.
const crypto = require('node:crypto');
const express = require('express');
const app = express();
app.use((req, res, next) => {
// Honour an ID from an upstream proxy if there is one, otherwise mint one.
req.id = req.get('x-request-id') || crypto.randomUUID();
res.setHeader('x-request-id', req.id);
const start = process.hrtime.bigint();
res.on('finish', () => {
const ms = Number(process.hrtime.bigint() - start) / 1e6;
console.log(JSON.stringify({
id: req.id,
method: req.method,
path: req.path,
status: res.statusCode,
ms: Math.round(ms),
}));
});
next();
});
app.get('/orders/:id', (req, res) => res.json({ ok: true }));
console.log('routes registered:', app._router === undefined ? 'lazy' : 'eager');Skipping the entropy cache
Both UUIDs are equally unpredictable. disableEntropyCache only changes where the bytes come from, trading a little speed for a smaller resident buffer — worth setting in a short-lived process or a memory-constrained worker that mints one or two IDs and exits.
const crypto = require('node:crypto');
// Default: Node pre-generates randomness for up to 128 UUIDs at a time.
const cached = crypto.randomUUID();
// Bypass that buffer and read fresh entropy for this one call.
const uncached = crypto.randomUUID({ disableEntropyCache: true });
console.log('both are valid v4 UUIDs:', cached[14] === '4' && uncached[14] === '4');
console.log('both are 36 chars:', cached.length === 36 && uncached.length === 36);
console.log('they differ:', cached !== uncached);
// Neither value is more or less random than the other — the cache is purely
// a throughput optimisation, not a reduction in entropy.
console.log('same format:', /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(uncached));
Discussion