BlogNode.js

Catching MongoDB E11000 duplicate key error in Node.js

Written by Codemzy on August 16th, 2024

Unique indexes are useful in MongoDB for preventing duplicate data. Here's how I catch the duplicate key error so I can send a human-readable response to let users know what the problem is.

The E11000 duplicate key error happens in MongoDB if you have a unique index on a field, and you try to add that value twice.

Probably the most common scenario for this is login information. If you identify users by their email address, and two people register with the same email address, that will cause problems. Which user is trying to log in? Which password should you check?

With a unique index in MongoDB, you can prevent duplicate entries for your unique field. Instead, you'll get an error thrown if the entry for a field is not unique.

That might be enough, but sometimes you'll want to know if the error is because of the unique field, or because of something else. So you can tell the end user what the problem is.

Here's how that might look:

await db.collection('users').insertOne({
  email: "you@youremail.com",
  pasword: "***********************",
}).catch((error) => {
  if (error.code === 11000) {
    throw new Error("User already exists!");
  } else {
    console.log(error); // log the unexpected error
    throw new Error("There was a problem!");
  }
});

We are catching the error, checking if it's because of a duplicate key (error code 11000 in MongoDB), and throwing a readable error.

Example

Let's say you're building an app for a car sales team, and they will add cars to the database for sale. You never want to add the same car twice, so you put a unique index on the vin field.

When a new car is ready for sale, you'll add it to the database with extra information about the vehicle.

await db.collection('cars').insertOne({
  vin: "WBABW33426PX70804",
  make: "BMW",
  model: "7 Series",
  year: 2006,
  mileage: 101245,
});

To save your team from fetching all this extra information unnecessarily, you're going to start by just asking for the VIN (vehicle identification number), and then proceed to collect all the extra information if you don't already have it.

await db.collection('cars').insertOne({
  vin: "WBABW33426PX70804"
});

But what if this car was already in the database? It would be added twice! And we don't want the same car added twice. Let's fix that by creating a unique index that tells MongoDB that each VIN is unique.

Creating the unique index

Before we can catch the E11000 duplicate key error, we need to make sure we have a unique index for the unique field in our collection.

I set this type of index anytime I need a field to be unique - things like email addresses for log-ins, national insurance numbers, and in this case... the car VIN!

db.collection('cars').createIndex( { "vin": 1 }, { unique: true } );

Now you should get an error anytime you try to add the same car twice.

Catching the error

Now we have a unique index on our vin field, if we try to add a car that already exists, our code will throw an error.

await db.collection('cars').insertOne({
  vin: "WBABW33426PX70804"
});

But we don't want to return the full MongoDB error to our users, because the message probably won't be very human-readable. For example, the duplicate key message looks something like this:

"E11000 duplicate key error collection: db.cars
 index: vin_1
 dup key: { vin: "WBABW33426PX70804" }"

And of course, MongoDB can throw other errors too. We don't want those all going back to the end user.

When we try to add a new car to our cars collection in MongoDB, we can catch the error like this:

await db.collection('cars').insertOne({
  vin: "WBABW33426PX70804"
}).catch((error) => {
  throw new Error("There was a problem!");
});

"There was a problem!" is much simpler!

But we don't know what kind of error. We can assume it's because the car already exists, but for all we know it's some other MongoDB error.

It's much better to look for the duplicate key error since we expect that error to happen occasionally, and then we can let the user know with a simplified error message.

Catching the duplicate key error

The error code for the MongoDB duplicate key is 11000, so we need our error handler to check for that code.

We can add an if statement to check for the duplicate key:

await db.collection('cars').insertOne({
  vin: "WBABW33426PX70804"
}).catch((error) => {
  if (error.code === 11000) {
    throw new Error("VIN already exists!");
  } else {
    console.log(error); // log the unexpected error
    throw new Error("There was a problem!");
  }
});

If the error code matches 11000, we know the error happened because the VIN already exists in the database, and we can let the user know!

If it's some other error we are not expecting, we can log it for further investigation, and let the user know there has been a problem.