ObjectId and the _id Field

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.

Taking an ObjectId apartJavaScript
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());
Output
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.