Reading data from MongoDB in Node.js uses collection.findOne() to fetch a single matching document or collection.find() to obtain a cursor over multiple documents. Applying field projections removes heavy or sensitive fields like passwordHash before returning JSON to API clients.
Note a critical distinction in the Node.js driver: await collection.findOne(...) executes immediately and returns a document (or null), whereas collection.find(...) returns a synchronous FindCursor—you must chain .toArray() or iterate over it with for await...of.
import { MongoClient } from 'mongodb';
const client = new MongoClient(process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017');
async function readCatalog() {
try {
await client.connect();
const users = client.db('kodersolution_store').collection('users');
// 1. Fetch a single user while excluding passwordHash
const profile = await users.findOne(
{ email: '[email protected]' },
{ projection: { passwordHash: 0 } }
);
console.log('User profile:', profile);
// 2. Fetch multiple active users using inclusion projection
const activeList = await users
.find({ isActive: true }, { projection: { fullName: 1, email: 1, _id: 0 } })
.limit(20)
.toArray();
console.log('Active users:', activeList);
} finally {
await client.close();
}
}
readCatalog().catch(console.error);
In MongoDB projections, 1 includes a field and 0 excludes it. You cannot mix 1 and 0 in the same projection object—the single exception is { fullName: 1, _id: 0 }, which lets you suppress _id alongside included fields.
await or .toArray() on collection.find(): Writing const docs = await collection.find({}) gives you a FindCursor object instead of an array of documents! Always append .toArray() (with .limit()) or stream with for await (const doc of cursor)..find({}).toArray() without .limit(): Loading an entire 500,000-document collection into a single Node.js array crashes the process with out-of-memory errors.passwordHash in API responses: Always specify { projection: { passwordHash: 0 } } (or an explicit allowlist of safe public fields) when querying user accounts.findOne() return when no document matches?It returns null, so always check if (!user) before reading properties like user.email.
.toArray()?Iterate directly over the cursor using for await (const doc of collection.find({})) { ... }, which fetches batches lazily from MongoDB without holding every document in RAM.
Use await collection.countDocuments({ isActive: true }) instead of loading documents into an array and checking .length.
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With
Experiment with the code from this lesson in our interactive playground.