Insert Documents
MongoDB provides three methods for inserting documents into a collection: insertOne(), insertMany(), and the legacy insert(). The insertOne() and insertMany() methods were introduced in MongoDB 3.2 and are the recommended approach for all new code. The older insert() method is deprecated and should not be used.
If the target collection does not exist when you call an insert method, MongoDB creates it automatically before inserting.
insertOne()
insertOne() inserts a single document into a collection. It returns an InsertOneResult object containing:
acknowledged—trueif the write was acknowledged by the server.insertedId— the_idof the inserted document (auto-generatedObjectIdif you did not provide one).
insertOne() example
// Insert a single user document
const result = db.users.insertOne({
name: "Alice",
email: "alice@example.com",
age: 28,
role: "admin",
active: true,
createdAt: new Date()
})
// MongoDB auto-generates _id when you don't provide one
console.log(result.insertedId) // ObjectId("64f1a2b3...")insertOne() result
{
acknowledged: true,
insertedId: ObjectId("64f1a2b3c4d5e6f7a8b9c0d1")
}insertMany()
insertMany() inserts an array of documents in a single operation. It returns an InsertManyResult with:
acknowledged—trueif the write was acknowledged.insertedIds— a map of array index →_idfor each inserted document.
Sending multiple documents in one insertMany() call is significantly more efficient than looping with insertOne() because the documents are batched into fewer network round-trips.
insertMany() example
const result = db.products.insertMany([
{
sku: "WIDGET-42",
name: "Blue Widget",
price: 9.99,
stock: 500,
category: "widgets",
tags: ["sale", "popular"],
createdAt: new Date()
},
{
sku: "GADGET-7",
name: "Red Gadget",
price: 24.50,
stock: 120,
category: "gadgets",
tags: ["new"],
createdAt: new Date()
},
{
sku: "DOOHICKEY-1",
name: "Purple Doohickey",
price: 4.99,
stock: 1_000,
category: "accessories",
tags: [],
createdAt: new Date()
}
])insertMany() result
{
acknowledged: true,
insertedIds: {
'0': ObjectId("64f1a2b3c4d5e6f7a8b9c0d2"),
'1': ObjectId("64f1a2b3c4d5e6f7a8b9c0d3"),
'2': ObjectId("64f1a2b3c4d5e6f7a8b9c0d4")
}
}Ordered vs Unordered Inserts
By default, insertMany() operates in ordered mode: it processes documents sequentially and stops at the first error, leaving the successfully inserted documents in place. Set ordered: false to switch to unordered mode, which continues processing all documents even when some fail.
Unordered insertMany
// With ordered: false, MongoDB inserts all valid documents
// and reports errors at the end — it does not stop on the first failure.
const result = db.users.insertMany(
[
{ _id: 1, name: "Bob", email: "bob@example.com" },
{ _id: 1, name: "Dupe", email: "dupe@example.com" }, // duplicate _id — will error
{ _id: 2, name: "Carol", email: "carol@example.com" }
],
{ ordered: false }
)
// Documents 1 and 3 are inserted; document 2 fails with E11000.
// A BulkWriteError is thrown but insertedIds still contains the successes.Mode | Behavior | Use Case |
|---|---|---|
ordered: true (default) | Stops at first error. Documents after the failed one are not inserted. | When atomicity matters — e.g., importing a ledger where partial success is worse than no success. |
ordered: false | Continues past errors. All valid documents are inserted; errors are collected and thrown together. | When partial success is acceptable — e.g., bulk-importing records where some may be duplicates. |
Providing Your Own _id
You can supply any unique BSON value as _id. This is useful when you already have a natural key (username, SKU, ISO code) and want to avoid the overhead of a secondary unique index.
Custom _id values
// String _id — natural key from a business domain
db.countries.insertOne({ _id: "DE", name: "Germany", continent: "Europe" })
// Integer _id — deterministic, human-readable
db.settings.insertOne({ _id: 1, featureFlags: { darkMode: true, beta: false } })
// Composite _id — compound natural key as an embedded document
db.inventory.insertMany([
{ _id: { warehouse: "NYC", sku: "WIDGET-42" }, qty: 500 },
{ _id: { warehouse: "LAX", sku: "WIDGET-42" }, qty: 200 }
])_id already exists in the collection, MongoDB throws WriteError: E11000 duplicate key error collection. Always wrap inserts that use application-managed _id values in a try/catch block.Inserting with Schema Validation
If a collection has a validator defined, MongoDB checks every inserted document against it. A document that fails validation is rejected (with validationAction: "error") or inserted with a warning (with validationAction: "warn").
Handling validation errors
try {
db.users.insertOne({
name: "Bob",
// email is required by the schema — omitting it causes a validation error
age: -5 // also invalid: age minimum is 0
})
} catch (e) {
if (e.code === 121) {
// WriteError code 121 = DocumentValidationFailure
console.error("Document failed schema validation:", e.message)
// "Document failed validation" + details about which rule failed
} else {
throw e // unexpected error — rethrow
}
}Bulk Writes
For high-performance scenarios that mix inserts, updates, and deletes in a single round-trip, use bulkWrite(). It accepts an array of write operation descriptors and executes them efficiently as a single batch.
bulkWrite() for batch operations
const result = db.inventory.bulkWrite([
// Insert a new document
{
insertOne: {
document: { _id: { warehouse: "CHI", sku: "GADGET-7" }, qty: 300 }
}
},
// Update an existing document
{
updateOne: {
filter: { _id: { warehouse: "NYC", sku: "WIDGET-42" } },
update: { $inc: { qty: -10 } }
}
},
// Delete a document
{
deleteOne: {
filter: { _id: { warehouse: "LAX", sku: "WIDGET-42" } }
}
},
// Upsert — insert if not found, update if found
{
updateOne: {
filter: { _id: { warehouse: "SEA", sku: "DOOHICKEY-1" } },
update: { $setOnInsert: { qty: 150, createdAt: new Date() } },
upsert: true
}
}
], { ordered: true })
console.log(result.insertedCount) // 1
console.log(result.modifiedCount) // 1
console.log(result.deletedCount) // 1
console.log(result.upsertedCount) // 1Write Concern
The write concern controls when MongoDB considers a write operation complete and acknowledges it back to the client. Choosing the right write concern is a tradeoff between durability and latency.
Write concern options
// Default: wait for the primary to acknowledge (w: 1)
db.orders.insertOne({ item: "Widget", qty: 10 })
// Majority write concern: wait until a majority of replica set members
// have written the document — much stronger durability guarantee
db.orders.insertOne(
{ item: "Widget", qty: 10 },
{ writeConcern: { w: "majority", wtimeout: 5000 } }
)
// Journal acknowledgement: wait until the write is flushed to the journal
db.orders.insertOne(
{ item: "Widget", qty: 10 },
{ writeConcern: { w: 1, j: true } }
)
// Fire and forget: no acknowledgement — fastest but no durability
db.logs.insertOne(
{ level: "debug", msg: "trace event" },
{ writeConcern: { w: 0 } }
)insertMany() returns an object where insertedIds is a map (index → ObjectId), not an array. To get a specific inserted ID: result.insertedIds[0], result.insertedIds[1], etc.insertMany() once per batch. A single insertMany() with 1 000 documents is orders of magnitude faster than 1 000 individual insertOne() calls because it dramatically reduces network round-trips and server overhead.Example Insert Session
> use ecommerce
switched to db ecommerce
> db.products.insertOne({ name: "Keyboard", price: 79.99, stock: 50 })
{
acknowledged: true,
insertedId: ObjectId('64f2c3d4e5f6a7b8c9d0e1f2')
}
> db.products.insertMany([
... { name: "Mouse", price: 29.99, stock: 120 },
... { name: "Monitor", price: 349.99, stock: 15 }
... ])
{
acknowledged: true,
insertedIds: {
'0': ObjectId('64f2c3d4e5f6a7b8c9d0e1f3'),
'1': ObjectId('64f2c3d4e5f6a7b8c9d0e1f4')
}
}
> db.products.countDocuments()
3