insertMany
When you need to add more than one document to a MongoDB collection, calling insertOne() in a loop works, but it sends one network round trip per document. insertMany() instead sends a single command containing an array of documents, letting the server write them as a batch. This is the standard way to seed data, import records, or write the results of a batch job into a collection.
Overview / How it works
A MongoDB collection stores BSON documents, and BSON (Binary JSON) is a superset of JSON with extra types like ObjectId, Date, Decimal128, and binary data. insertMany() takes a JavaScript array of plain object literals, converts each one to BSON, assigns an _id field to any document that doesn’t already have one, and writes them to the collection’s storage engine (WiredTiger) as part of one command.
Because a collection has no fixed schema, the documents in a single insertMany() call don’t need matching fields — you could insert a product with a color field and one without it in the same call. That flexibility is convenient, but it also means MongoDB won’t catch a missing required field or a wrong data type for you; if your application depends on documents having a certain shape, you enforce that with schema validation or careful application code, not by relying on the database to reject bad shapes automatically.
The key behavioral question with insertMany() is what happens when one document in the batch fails to insert — for example, a duplicate _id collides with an existing document, or a value violates a validation rule. By default MongoDB performs an ordered insert: documents are inserted in array order, and the first failure stops the operation, leaving any documents after it in the array uninserted. Pass { ordered: false } and MongoDB attempts every document, continuing past failures and reporting all the errors at the end. Unordered inserts can also be parallelized more aggressively by the server since it isn’t required to preserve strict sequencing.
Syntax
db.collection.insertMany(
[ , , ... ],
{
ordered: ,
writeConcern:
}
);
| Parameter | Description |
|---|---|
[ |
Required. An array of one or more plain JavaScript object literals to insert. Passing a single object instead of an array throws an error. |
ordered |
Optional, default true. When true, MongoDB stops at the first write error and skips the rest of the array. When false, MongoDB attempts all documents and reports every error together. |
writeConcern |
Optional. Overrides the collection’s default write concern, e.g. { w: "majority" } to require acknowledgment from a majority of replica set members before returning. |
On success, insertMany() returns a document with acknowledged: true and an insertedIds object mapping each array index to the ObjectId (or custom _id) that was assigned.
Examples
Example 1: Basic bulk insert
db.products.insertMany([
{ name: "Wireless Mouse", price: 25.99, category: "Electronics", inStock: true },
{ name: "Mechanical Keyboard", price: 89.99, category: "Electronics", inStock: true },
{ name: "USB-C Hub", price: 34.50, category: "Electronics", inStock: false }
]);
Output:
{
acknowledged: true,
insertedIds: {
'0': ObjectId('64f1a2b3c4d5e6f7a8b9c0d1'),
'1': ObjectId('64f1a2b3c4d5e6f7a8b9c0d2'),
'2': ObjectId('64f1a2b3c4d5e6f7a8b9c0d3')
}
}
All three documents lacked an _id field, so MongoDB generated a unique ObjectId for each one. The insertedIds object lets you correlate array positions with the generated ids, which is useful if you need to reference these documents right after inserting them.
Example 2: Unordered insert with a duplicate key error
Suppose a document with _id: 100 already exists in db.products. Now we try to insert a batch that includes another document using that same _id:
db.products.insertMany(
[
{ _id: 100, name: "Conflicting Item", price: 5.00 },
{ name: "Wireless Charger", price: 19.99 },
{ name: "Phone Case", price: 12.99 }
],
{ ordered: false }
);
Output:
MongoBulkWriteError: E11000 duplicate key error collection: shop.products index: _id_ dup key: { _id: 100 }
Result: BulkWriteResult {
insertedCount: 2,
insertedIds: {
'1': ObjectId('64f1a2b3c4d5e6f7a8b9c0d5'),
'2': ObjectId('64f1a2b3c4d5e6f7a8b9c0d6')
}
}
Because ordered is false, MongoDB skipped only the failing document (index 0) and still inserted the other two. The shell throws an error object describing the failure, but the operation’s result still reports which documents succeeded. Had we left ordered at its default of true, the second and third documents would never have been attempted.
Example 3: Generating and inserting many documents at once
const newOrders = Array.from({ length: 5 }, (_, i) => ({
orderNumber: `ORD-2026-${1000 + i}`,
customer: "jane.doe@example.com",
total: 49.99 + i * 10,
status: "pending",
createdAt: new Date()
}));
db.orders.insertMany(newOrders);
Output:
{
acknowledged: true,
insertedIds: {
'0': ObjectId('64f1b7a1c4d5e6f7a8b9c1a0'),
'1': ObjectId('64f1b7a1c4d5e6f7a8b9c1a1'),
'2': ObjectId('64f1b7a1c4d5e6f7a8b9c1a2'),
'3': ObjectId('64f1b7a1c4d5e6f7a8b9c1a3'),
'4': ObjectId('64f1b7a1c4d5e6f7a8b9c1a4')
}
}
This is a common real-world pattern: build a JavaScript array in memory (from generated data, a parsed CSV, or an API response) and hand the whole array to a single insertMany() call instead of looping with insertOne().
How it works step by step
- The driver serializes the JavaScript array into a single
insertcommand containing a batch of BSON documents. - For any document missing an
_id, the driver generates anObjectIdclient-side before sending the command — MongoDB does not wait to assign ids server-side. - Very large arrays are split by the driver into multiple sub-batches under the hood to respect BSON’s per-document size limit (16MB) and the maximum message size, but from your code’s perspective it’s still one logical
insertMany()call. - The server writes each document to the WiredTiger storage engine and updates every index defined on the collection (including the default
_idindex) as each document is stored. - Each individual document insert is atomic, but
insertMany()as a whole is not one atomic transaction across documents — withordered: truea failure partway through leaves earlier documents inserted and later ones skipped; withordered: falseonly the failing documents are skipped. - On a replica set, the write is recorded in the oplog and replicated to secondaries; the
writeConcernoption controls how many members must acknowledge the write before the command returns.
Common Mistakes
Mistake 1: Passing a single object instead of an array.
db.products.insertMany({ name: "Solo Item", price: 10 });
This throws an error because insertMany() expects an array, even for a single document. Use insertOne() for a single document, or wrap it in an array:
db.products.insertMany([{ name: "Solo Item", price: 10 }]);
Mistake 2: Expecting all documents to insert despite a duplicate key, without setting ordered: false.
db.products.insertMany([
{ _id: 100, name: "Duplicate", price: 5.00 },
{ name: "Item A", price: 15.00 },
{ name: "Item B", price: 22.00 }
]);
// If _id 100 already exists, "Item A" and "Item B" are NEVER inserted
// because the default ordered:true stops at the first failure.
If partial success with all-errors-reported is what you actually want, be explicit about it:
db.products.insertMany(
[
{ _id: 100, name: "Duplicate", price: 5.00 },
{ name: "Item A", price: 15.00 },
{ name: "Item B", price: 22.00 }
],
{ ordered: false }
);
Best Practices
- Use
insertMany()instead of a loop ofinsertOne()calls whenever you’re writing more than a handful of documents — it’s dramatically fewer network round trips. - Default to
ordered: truewhen insert order and fail-fast behavior matter (e.g. dependent records); useordered: falsefor bulk imports where you want to load everything that’s valid and inspect the errors afterward. - Let MongoDB generate
_idvalues unless you have a real reason to supply your own; if you do supply your own, make sure they’re actually unique before the call. - Validate and sanitize documents in application code before insertion — MongoDB’s flexible schema won’t stop you from inserting inconsistent or malformed data.
- For very large imports (tens of thousands of documents or more), consider chunking the array yourself and inserting in smaller batches to keep memory usage and command size predictable.
- Check the write result (
insertedCount,insertedIds) rather than assuming success, especially withordered: falsewhere partial failures are expected behavior, not a bug.
Practice Exercises
- Create a
db.studentscollection and insert five student documents in oneinsertMany()call, each withname,grade, and an array fieldsubjects. Confirm all five ids appear in the result’sinsertedIds. - Insert three documents into a collection where the second document intentionally reuses an existing
_id. Run it once with the default ordered behavior and once with{ ordered: false }, and compare how many documents actually get inserted each time. - Generate an array of 20 documents programmatically (using
Array.fromor a loop) representing log entries with atimestampandlevelfield, insert them withinsertMany(), then usecountDocuments()to confirm all 20 were written.
Summary
insertMany()inserts an array of documents in a single command, avoiding one round trip per document.- It returns
{ acknowledged, insertedIds }on success, mapping array indexes to generated or supplied_idvalues. - The default
ordered: truestops at the first error and skips remaining documents;ordered: falseattempts every document and reports all errors together. - Each document’s write is atomic individually, but
insertMany()as a whole is not a single atomic transaction across documents. - MongoDB’s flexible schema means
insertMany()won’t reject mismatched document shapes — validate in your application or with schema validation rules. - Always pass an array, even for a single document, or the call throws an error.
