Coming from SQL, the first MongoDB question is usually "where is CREATE TABLE?" There is not one, and that throws people. You do not declare a schema before you use a collection. So how do you create a schema in MongoDB? Three ways, and real projects use a mix of them. Here is each, and when it is the right one.
1. The implicit schema: just insert
A collection comes into existence the first time you insert into it. No declaration, no migration, no CREATE anything:
db.users.insertOne({ email: "sam@example.com", createdAt: new Date() })
That single write creates the users collection, and its schema is now "documents that look like this." Your data has a shape whether or not you declared it. This is the fastest way to start and the reason MongoDB feels quick early on. It is also why, with nobody minding the shape, schema drift creeps in later. Convenient now, a phone call at 2am eventually.
If you want the collection to exist before any insert (to attach options), create it explicitly:
db.createCollection("users")
2. The explicit schema: a validator
The closest thing to CREATE TABLE is a collection with a $jsonSchema validator. You declare the required fields and their types, and MongoDB rejects writes that do not match:
db.createCollection("users", {
validator: { $jsonSchema: {
bsonType: "object",
required: ["email", "createdAt"],
properties: {
email: { bsonType: "string" },
createdAt: { bsonType: "date" }
}
} }
})
Now the shape is enforced by the database, not just implied by habit. This is how you make a schema firm once you have decided on it, and it is worth a post of its own: see MongoDB JSON Schema validation for the levels, the warn-then-error rollout, and the default-values gotcha.
3. The application schema: an ODM
Most application code defines the schema a third way, in the app itself, through an Object Document Mapper. Mongoose is the common one in Node:
const User = mongoose.model("User", new mongoose.Schema({
email: { type: String, required: true },
createdAt: { type: Date, default: Date.now }
}))
This gives you defaults, casting, and validation in code, close to where you write the queries. It does not enforce anything at the database level, though, so a script or another service writing directly to the collection can still insert whatever it likes. That is why the belt-and-suspenders answer is an ODM in the app plus a validator on the collection.
Design it, then keep it honest
Creating a schema is the easy part. Creating a good one is MongoDB schema design: modeling around your queries, deciding what to embed and what to reference. And because a flexible schema drifts, the ongoing job is watching the real shape of your data so it still matches what you intended, which is schema analysis.
mongoui's Schema doctor reads the implicit schema straight from your documents: it samples a collection and shows you the actual field shapes, types, and the drift a schemaless database quietly allows. It runs on your machine, so nothing you sample leaves your network. Insert to create it, validate to enforce it, and check now and then that the shape you have is the shape you meant.
