Can I determine if a string is a MongoDB ObjectID?

I found that the mongoose ObjectId validator works to validate valid objectIds but I found a few cases where invalid ids were considered valid. (eg: any 12 characters long string)

var ObjectId = require('mongoose').Types.ObjectId;
ObjectId.isValid('microsoft123'); //true
ObjectId.isValid('timtomtamted'); //true
ObjectId.isValid('551137c2f9e1fac808a5f572'); //true

What has been working for me is casting a string to an objectId and then checking that the original string matches the string value of the objectId.

new ObjectId('timtamtomted'); //616273656e6365576f726b73
new ObjectId('537eed02ed345b2e039652d2') //537eed02ed345b2e039652d2

This work because valid ids do not change when casted to an ObjectId but a string that gets a false valid will change when casted to an objectId.


You can use a regular expression to test for that:

CoffeeScript

if id.match /^[0-9a-fA-F]{24}$/
    # it's an ObjectID
else
    # nope

JavaScript

if (id.match(/^[0-9a-fA-F]{24}$/)) {
    // it's an ObjectID    
} else {
    // nope    
}

✅ Build In Solution isValidObjectId() > Mongoose 5.7.12

If you are using Mongoose, we can test whether a String is of 12 bytes or a string of 24 hex characters by using mongoose build-in isValidObjectId.

mongoose.isValidObjectId(string); /* will return true/false */

🛑 DO NOTE!

isValidObjectId() is most commonly used to test a expected objectID, in order to avoid mongoose throwing invalid object ID error.

Example

if (mongoose.isValidObjectId("some 12 byte string")) {
     return collection.findOne({ _id: "some 12 byte string" })
     // returns null if no record found.
}

If you do not conditionally test whether expected objectID is valid, you will need to catch the error.

try {
  return collection.findOne({ _id: "abc" }) 
  //this will throw error
} catch(error) {
  console.log('invalid _id error', error)
}

Since findOne({ _id: null }) and findOne({ _id: undefined }) are completely valid queries (doesn't throw error), isValidObjectId(undefined) and isValidObjectId(null) will return true.

🛑 DO NOTE 2!

123456789012 may not appear to look like a bson string but it's completely a valid ObjectID because the following query does not throw error. (return null if no record found).

findOne({ _id: ObjectId('123456789012')}) // ✅ valid query

313233343536373839303132 may appear to look like a 24 character string (it's the hex value of 123456789012), but it's also a valid ObjectId because the following query does not throw error. (return null if no record found)

findOne({ _id: ObjectId('313233343536373839303132')}) // ✅ valid query

The following are invalid (1 string char less than above examples)

findOne({ _id: ObjectId('12345678901')}) // ❌ not 12 byte string
findOne({ _id: ObjectId('31323334353637383930313')}) // ❌ not 24 char hex

Format of ObjectId

ObjectIds are small, likely unique, fast to generate, and ordered. ObjectId values are 12 bytes in length, consisting of:

  • a 4-byte timestamp value, representing the ObjectId's creation, measured in seconds since the Unix epoch
  • a 5-byte random value generated once per process. This random value is unique to the machine and process.
  • a 3-byte incrementing counter, initialized to a random value

Due to the above random value, ObjectId cannot be calculated. It can only appear to be a 12 byte string, or 24 character hex string.


I have used the native node mongodb driver to do this in the past. The isValid method checks that the value is a valid BSON ObjectId. See the documentation here.

var ObjectID = require('mongodb').ObjectID;
console.log( ObjectID.isValid(12345) );