Every document has an _id: the primary key, unique in the collection, indexed automatically, immutable once written. Omit it and the driver generates an ObjectId on the client before the insert leaves your process, which is why an insert reports the new id without a round trip.
An ObjectId is 12 bytes printed as 24 hex characters: a 4-byte big-endian Unix timestamp in seconds, a 5-byte value random per process, and a 3-byte counter that starts randomly and increments per id. No coordinator, no collisions in practice, and — the timestamp coming first — ids sort roughly by creation time.
import { ObjectId } from 'bson';
const id = new ObjectId(), b = id.id; // b holds the raw 12 bytes
console.log('hex :', id.toHexString(), '|', id.getTimestamp().toISOString());
console.log('fields :', Buffer.from(b.subarray(0, 4)).toString('hex'),
Buffer.from(b.subarray(4, 9)).toString('hex'), Buffer.from(b.subarray(9)).toString('hex'));
console.log('next two :', new ObjectId().toHexString(), new ObjectId().toHexString());
console.log('cutoff :', ObjectId.createFromTime(Date.UTC(2026, 0, 1) / 1000).toHexString());hex : 6ab1be72f2a355d298cdafa5 | 2026-09-21T23:32:02.000Z fields : 6ab1be72 f2a355d298 cdafa5 next two : 6ab1be72f2a355d298cdafa6 6ab1be72f2a355d298cdafa7 cutoff : 6955b9000000000000000000
The counter increments visibly across the three ids. The last line is a useful trick: createFromTime builds an id with the timestamp you ask for and nine zero bytes, so { _id: { $gte: ObjectId.createFromTime(t) } } selects documents created on or after t through the _id index alone — no createdAt field, no extra index. That timestamp has one-second resolution and comes from the client's clock, so it is no audit trail.
An ObjectId is not a string: doc._id === req.params.id is always false. Use id.equals(other), and validate untrusted input with ObjectId.isValid. _id may be any BSON type except an array, and a natural key you already have — a country code, a SKU — saves an index and a lookup.