0Pricing
MongoDB Academy · Lesson

insertOne and insertMany

Learners will insert single and bulk documents and inspect the auto-generated _id field returned by each operation.

insertOne and insertMany is a free MongoDB Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the MongoDB Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

The Two Insert Methods

MongoDB provides two methods for adding documents to a collection:

  • insertOne(doc) — inserts a single document and returns an object with acknowledged and insertedId
  • insertMany(docs) — inserts an array of documents and returns acknowledged and insertedIds (an object mapping array index to generated _id)

Both methods are synchronous in mongosh and return Promises in Node.js driver code. Always await them in async Node.js functions to handle errors properly.

// insertOne
const res1 = await db.collection('users').insertOne({ name: 'Alice', age: 28 });
console.log(res1.insertedId); // ObjectId('...')

// insertMany
const res2 = await db.collection('users').insertMany([
  { name: 'Bob', age: 32 },
  { name: 'Carol', age: 25 }
]);
console.log(res2.insertedIds);
// { '0': ObjectId('...'), '1': ObjectId('...') }

Auto-Generated ObjectId

When you insert a document without an _id field, MongoDB generates an ObjectId automatically. The ObjectId is a 12-byte BSON type whose structure ensures global uniqueness across distributed nodes without a central counter:

  • Bytes 0-3: Unix timestamp (seconds) — allows approximate sort by insertion time
  • Bytes 4-8: Random value (generated once per process)
  • Bytes 9-11: Incrementing counter (initialized to a random value)

This design means two different servers inserting documents at the same millisecond will still produce different ObjectIds, making distributed inserts collision-free.

// Auto-generated ObjectId on insert
db.items.insertOne({ name: 'Widget' });
// { acknowledged: true, insertedId: ObjectId('64a2f3b1c9e7e12345678901') }

// Extract timestamp from an ObjectId
const id = ObjectId('64a2f3b1c9e7e12345678901');
print(id.getTimestamp());
// ISODate('2023-07-03T10:15:29.000Z')

// Sorting by _id gives approximate insertion order
db.items.find({}).sort({ _id: 1 });

Supplying Your Own _id

You can override the auto-generated ObjectId by providing your own _id value. Any BSON type works: string, number, UUID, or even a nested object. The only requirement is that the value must be unique within the collection.

Custom _ids are useful when you already have a natural unique key—a product SKU, a username slug, or an external system's ID. However, avoid monotonically incrementing integers as custom _ids in sharded clusters—they create write hot spots on the shard that holds the highest key range.

// String _id (natural key)
db.products.insertOne({
  _id: 'SKU-WH-BLK-42',
  name: 'Wireless Headphones Black',
  price: 79.99
});

// Number _id
db.categories.insertOne({ _id: 100, name: 'Electronics' });

// Duplicate _id throws E11000 duplicate key error:
db.categories.insertOne({ _id: 100, name: 'Gadgets' });
// MongoServerError: E11000 duplicate key error

insertMany: Bulk Inserts

insertMany accepts an array of documents. It is significantly more efficient than calling insertOne in a loop because it sends all documents in a single network round-trip and MongoDB can batch the writes internally.

By default, insertMany inserts documents in ordered mode: if one document fails validation or has a duplicate _id, the operation stops and does not insert remaining documents. Set { ordered: false } as the second argument to switch to unordered mode, where MongoDB continues inserting the rest even if some fail.

// Ordered insertMany (default) - stops on first error
db.users.insertMany([
  { name: 'Alice' },
  { name: 'Bob' },
  { name: 'Carol' }
]);

// Unordered insertMany - continues after errors
db.users.insertMany(
  [
    { _id: 1, name: 'Alice' },
    { _id: 1, name: 'Duplicate' },  // Will fail (dup key)
    { _id: 2, name: 'Bob' }         // Still inserted in unordered mode
  ],
  { ordered: false }
);

Inspecting the insertedIds Object

The result object from insertMany contains insertedIds as an object (not an array) where keys are the array index strings and values are the generated ObjectIds. This lets you correlate each input document with its resulting _id.

In Node.js, you often map the inserted documents back to their IDs for further processing—for example, creating related documents in another collection that reference these new _ids.

const docs = [
  { name: 'Widget A', price: 10 },
  { name: 'Widget B', price: 20 },
  { name: 'Widget C', price: 30 }
];

const result = await db.collection('products').insertMany(docs);

console.log(result.insertedCount); // 3
console.log(result.insertedIds);
// {
//   '0': ObjectId('...'),
//   '1': ObjectId('...'),
//   '2': ObjectId('...')
// }

// Attach _ids back to original docs
docs.forEach((doc, i) => {
  doc._id = result.insertedIds[i];
});

WriteConcern: Controlling Durability

By default, insertOne and insertMany use the connection's default write concern—usually w: 1, meaning the primary acknowledges the write. You can override this per-operation:

  • { w: 0 } — fire-and-forget, no acknowledgment (fastest, unsafe)
  • { w: 1 } — acknowledged by primary (default)
  • { w: 'majority' } — acknowledged by majority of replica set members (safest)
  • { j: true } — additionally requires the write to be flushed to the journal on disk
// Safe insert: confirmed by majority of replica set + journaled
await db.collection('orders').insertOne(
  { orderId: 'ORD-001', total: 99.99, status: 'pending' },
  { writeConcern: { w: 'majority', j: true } }
);
// Slower than w:1 but guaranteed durable even if primary crashes

Handling Duplicate Key Errors

MongoDB throws a duplicate key error (E11000) when an insert violates a unique index—most commonly the _id uniqueness requirement, but also any user-defined unique index. In Node.js, this throws a MongoServerError with code 11000.

Always wrap inserts in try/catch when duplicate key errors are expected (e.g., upserting users by email). Check the error code to distinguish duplicates from other write errors and respond appropriately—return 409 Conflict from an API, or update the existing document instead.

try {
  await db.collection('users').insertOne({
    email: 'alice@example.com',
    name: 'Alice'
  });
} catch (err) {
  if (err.code === 11000) {
    // Duplicate key - email already exists
    console.log('User with this email already exists');
    // Respond with HTTP 409 Conflict in an API
  } else {
    throw err;  // Re-throw unexpected errors
  }
}

Seeding Test Data With Loops

A common development task is seeding a collection with realistic test data. You can do this directly in mongosh using a JavaScript loop, or write a Node.js script. For large datasets, use insertMany in batches of 500-1000 documents rather than one massive array—this avoids memory issues and allows progress monitoring.

For generating realistic fake data in Node.js, the @faker-js/faker library is widely used. It generates realistic names, emails, addresses, dates, and many other field types, making test data indistinguishable from real data.

// Seed 100 test users in mongosh
const users = [];
for (let i = 1; i <= 100; i++) {
  users.push({
    name: 'User ' + i,
    email: 'user' + i + '@example.com',
    age: 18 + (i % 50),
    active: i % 3 !== 0,
    createdAt: new Date(Date.now() - i * 86400000)
  });
}
db.users.insertMany(users);
// { acknowledged: true, insertedCount: 100 }

Checking Inserted Documents

After an insert, always verify your data landed correctly. Useful verification commands:

  • db.collection.countDocuments({}) — total count
  • db.collection.findOne({ _id: insertedId }) — verify a specific document by its returned _id
  • db.collection.find({}).sort({ _id: -1 }).limit(5) — see the 5 most recently inserted documents

In automated tests, verify not just that the insert succeeded (no error thrown) but also that the stored document has the expected field values—subtle type coercions or middleware transformations can change fields during insert.

// Insert and immediately verify
const { insertedId } = await db.collection('orders').insertOne({
  customerId: ObjectId('...'),
  items: [{ sku: 'A1', qty: 2 }],
  total: 39.98,
  status: 'pending'
});

const stored = await db.collection('orders').findOne({ _id: insertedId });
console.log(stored.status);   // 'pending'
console.log(stored.total);    // 39.98

Transactions and Multiple Inserts

When you need to insert documents into multiple collections atomically—all succeed or all fail together—use a multi-document transaction. Without a transaction, if the second insert fails after the first succeeds, your data is in an inconsistent state.

Transactions add latency and should be reserved for cases where atomicity is truly required. For most single-collection bulk inserts, insertMany with ordered mode provides sufficient atomicity at the collection level (either all documents before the failure are inserted, or none if rollback is needed).

// Atomic insert across two collections using a transaction
const session = client.startSession();
try {
  await session.withTransaction(async () => {
    await db.collection('orders').insertOne(
      { total: 99.99, customerId: custId }, { session }
    );
    await db.collection('orderEvents').insertOne(
      { type: 'created', orderId: ordId }, { session }
    );
  });
} finally {
  await session.endSession();
}

Performance Tip: Bulk Write API

For mixed operations (inserts, updates, deletes) in a single batch, use db.collection.bulkWrite(operations). This is more efficient than separate insertMany + updateMany calls because all operations share a single network round-trip.

Each operation in the array is an object with a key like insertOne, updateOne, deleteOne, etc. The result object summarizes inserted, modified, and deleted counts. bulkWrite also supports ordered/unordered modes just like insertMany.

await db.collection('products').bulkWrite([
  { insertOne: { document: { name: 'New Product', price: 25 } } },
  { updateOne: {
      filter: { name: 'Old Product' },
      update: { $set: { price: 15 } }
  }},
  { deleteOne: { filter: { name: 'Discontinued' } } }
]);
// { insertedCount: 1, modifiedCount: 1, deletedCount: 1 }

Quick Check

Test your understanding of MongoDB & NoSQL Databases concepts from this lesson.

Lesson Recap

In this lesson you learned: insertOne and insertMany are the two insert methods—insertMany is more efficient for bulk data and supports ordered/unordered error handling, ObjectId is auto-generated if you do not supply _id, encoding the insertion timestamp in its first 4 bytes for approximate ordering, and write concern controls durability from fire-and-forget (w:0) to majority-replicated (w:'majority'). Next up we explore the find() cursor in depth and learn how to query nested fields and arrays with dot notation.

Frequently asked questions

Is the “insertOne and insertMany” lesson free?

Yes — the full text of “insertOne and insertMany” is free to read here on the web, and the MongoDB Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the MongoDB Academy course, upgrade to CoddyKit PRO.

What will I learn in “insertOne and insertMany”?

Learners will insert single and bulk documents and inspect the auto-generated _id field returned by each operation. You practise MongoDB Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start MongoDB Academy?

No prior experience is required. MongoDB Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “insertOne and insertMany” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this MongoDB Academy lesson?

Yes. Every MongoDB Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. insertOne and insertMany
  2. findOne vs find: Cursors Explained
  3. Querying Nested Fields and Arrays
  4. Reading Documents With the Node.js Driver
← Back to MongoDB Academy