Skip to content
100% in your browser. Nothing you paste is uploaded — all processing runs locally. Read more →

UUID vs MongoDB ObjectId

On this page
  1. What ObjectId is
  2. Side-by-side
  3. Where ObjectId wins
  4. Where UUID v7 wins
  5. When to use which
  6. Storing UUIDs in MongoDB
  7. Code: timestamp from each
    1. ObjectId timestamp
    2. UUID v7 timestamp
  8. Migration: ObjectId → UUID v7
  9. Common pitfalls
  10. Decision flowchart
  11. Try the tools

TL;DR. MongoDB’s ObjectId is a 12-byte time-ordered ID specific to MongoDB. UUID v7 is a 16-byte time-ordered ID that’s a universal standard. Use ObjectId in pure-MongoDB systems for compactness; use UUID v7 anywhere else, including MongoDB if you cross system boundaries.

What ObjectId is

MongoDB’s ObjectId is a 12-byte (96-bit) value generated by MongoDB drivers. Layout:

┌────────────────┬──────────────────┬─────────────────┐
│ 4-byte         │ 5-byte           │ 3-byte          │
│ Unix-second    │ random per-      │ incrementing    │
│ timestamp      │ process value    │ counter         │
└────────────────┴──────────────────┴─────────────────┘

It’s MongoDB’s default _id for any collection where you don’t specify one. It’s been there since MongoDB 1.0 (2009).

Side-by-side

UUID v7MongoDB ObjectId
Bytes1612
Hex string length36 (with hyphens)24
Timestamp resolution1 millisecond1 second
Timestamp bits4832
Random bits7440 (per-process, 24 of which is a counter)
Universal standardRFC 9562 (IETF)MongoDB-specific
Coordinationnonenone
Sortableyes (lexicographic)yes (lexicographic)
Native in non-MongoDB DBsyesno

Where ObjectId wins

Where UUID v7 wins

When to use which

Single-system MongoDB, no integrations, you control all clients
└── ObjectId is fine — that's what it's for

MongoDB + other databases / message queues / external systems
└── UUID v7 (consistency at boundaries beats per-DB compactness)

Public-facing IDs (URLs, share tokens)
└── UUID v7 (the standard is recognizable; ObjectIds look like a leak of internal infra)

Greenfield project, MongoDB is one of multiple data stores
└── UUID v7 stored as BinData — same on-disk size as ObjectId in MongoDB,
    universal everywhere else

Storing UUIDs in MongoDB

MongoDB has a native BinData type for binary fields. UUIDs are 16 bytes — store them as BinData(4) (subtype 4 = UUID per RFC 4122):

import { Binary } from "mongodb";
import { v7 } from "uuid";

const id = v7();                                          // canonical string
const bytes = Buffer.from(id.replace(/-/g, ""), "hex");
const binId = new Binary(bytes, Binary.SUBTYPE_UUID);     // BinData(4, ...)

await collection.insertOne({ _id: binId, email: "alice@example.com" });

Or use bson.UUID if your driver supports it (Node’s official MongoDB driver does in 4.x+):

import { UUID } from "mongodb";

await collection.insertOne({ _id: new UUID(id), email });

This stores 16 bytes — same wire size as the 12-byte ObjectId in disk page bytes (the difference is amortized by row overhead). Indexes are slightly larger but typically not a bottleneck.

Code: timestamp from each

ObjectId timestamp

import { ObjectId } from "mongodb";

const id = new ObjectId();
id.getTimestamp();
// 2026-04-26T12:34:56.000Z (second precision)

UUID v7 timestamp

function v7Timestamp(uuid) {
  const hex = uuid.replace(/-/g, "").slice(0, 12);
  return new Date(Number(BigInt("0x" + hex)));
}

v7Timestamp("01928a47-3b30-7c5e-9d1a-f0b8c4a7e923");
// 2024-10-09T21:32:30.123Z (millisecond precision)

Both let you recover when a record was created. Useful for forensics, audit, debugging.

Migration: ObjectId → UUID v7

If you decide to migrate a MongoDB collection from ObjectId to UUID:

const cursor = collection.find({});
for await (const doc of cursor) {
  const newId = new UUID(uuidv7());
  await collection.insertOne({ ...doc, _id: newId, legacy_id: doc._id });
  // update foreign references in dependent collections, then:
  await collection.deleteOne({ _id: doc._id });
}

In practice, don’t backfill. Add UUIDs alongside the existing ObjectId for new documents, and update foreign-reference fields to use the UUID going forward. Mixed-mode is fine — the version (UUID v7 vs ObjectId) is detectable from the bytes.

Common pitfalls

Decision flowchart

Is MongoDB the only database in your stack?
├── Yes → ObjectId (you're already there)
└── No
    ├── Do you cross system boundaries with these IDs?
    │   ├── Yes → UUID v7
    │   └── No → ObjectId
    └── Are these IDs ever public-facing?
        └── Yes → UUID v7 (don't expose internal infra)

Are you greenfield with no preference?
└── UUID v7 (universal beats specialized for new systems in 2026)

Try the tools