Application data

MongoDB

Documents, collections, queries, indexes, and aggregation — how ReactBD stores users, tools, shops, and anything that is not a Sanity document.

DocumentsQueriesIndexesAggregation

Documents, not rows

A MongoDB document is BSON (binary JSON) with an _id. Nested objects and arrays are allowed. You model how you read, not how a SQL third-normal-form table would look.

tool.json
{
  "_id": "ObjectId(...)",
  "title": "Pattern Craft",
  "href": "https://bg.reactbd.com/",
  "featured": true,
  "category": "Design",
  "tags": ["backgrounds", "css"],
  "createdAt": "2026-01-12T00:00:00.000Z"
}

Collections

A collection is a bag of documents of one kind: users, tools, shops. Indexes live on the collection. Mixing unrelated shapes in one collection makes indexes and validation harder — don’t.

  • Name collections in plural, lowercase: tools, not Tool.
  • Put a unique index on fields you look up: email, slug.
  • Validation ($jsonSchema) is optional but useful in production.

CRUD

NeedMethod
Insert oneinsertOne(doc)
Insert manyinsertMany(docs)
Find onefindOne({ slug })
Find manyfind(filter).sort().limit()
UpdateupdateOne(filter, { $set: { ... } })
DeletedeleteOne(filter)
tools.ts
const { db } = await connectToDatabase();
const tools = db.collection("tools");

await tools.updateOne(
  { slug: "pattern-craft" },
  { $set: { featured: true, updatedAt: new Date() } },
  { upsert: false },
);

Queries

find filters, projection picks fields, sort + skip + limit paginate. Prefer indexed fields in the filter. Do not skip into deep pages on huge collections — use a range on _id or createdAt (cursor pagination).

find.js
db.tools.find(
  { featured: true, category: "Design" },
  { title: 1, href: 1, featured: 1 }
).sort({ createdAt: -1 }).limit(12)
  • Operators: $in, $gte, $regex (use sparingly — leading-wildcard regex cannot use an index well).
  • Projection { title: 1 } is inclusion. { passwordHash: 0 } is exclusion. Don’t mix both except _id.
  • Always define a sort when you paginate, or the order is undefined.

Indexes

Without an index, MongoDB scans the collection. With the right index, it jumps. Unique indexes also enforce uniqueness — that is your email constraint.

indexes.js
db.users.createIndex({ email: 1 }, { unique: true });
db.tools.createIndex({ featured: 1, createdAt: -1 });
db.tools.createIndex({ slug: 1 }, { unique: true });

Aggregation

Pipelines reshape data: $match → $group → $sort → $project. Use them for counts, category breakdowns, and reports — not for fetching a single document by slug.

category-counts.js
db.tools.aggregate([
  { $match: { published: true } },
  { $group: { _id: "$category", count: { $sum: 1 } } },
  { $sort: { _id: 1 } }
])

Put $match first so later stages see fewer documents. Index the match fields.

Schema design

RelationshipDefault
One-to-few (address on user)Embed.
One-to-many (products in a shop)Reference _ids, or embed if always read together and bounded.
Many-to-many (posts ↔ tags)Array of tag slugs if small; join collection if huge.
Events / logsSeparate collection. Never unbounded embed.

ReactBD stores tools, users, shops, community in Mongo. Editorial posts and courses live in Sanity (or Neon for some blog tables). Pick the store by who edits it and how it is queried.

Node driver

The official driver is enough. Mongoose adds schemas and middleware — fine if the team wants it; this repo often uses the driver plus a cached connection for serverless/Next.

connect.ts
import { MongoClient } from "mongodb";

const uri = process.env.MONGODB_URI!;
const globalForMongo = globalThis as unknown as { _client?: MongoClient };

export async function connectToDatabase() {
  const client =
    globalForMongo._client ?? new MongoClient(uri);
  if (!globalForMongo._client) {
    globalForMongo._client = client;
    await client.connect();
  }
  return { client, db: client.db() };
}

Local vs Atlas

  • Local: Docker or mongod for offline work. Same driver URI shape.
  • Atlas: managed replica set, IP allowlist or peering, backups.
  • Connection string includes user, cluster host, and database name. Treat it as a secret.
  • Never log the URI. Never put it in NEXT_PUBLIC_*.

How ReactBD uses MongoDB

  • Public tools catalog: GET /api/public/tools reads Mongo, featured-first in the UI.
  • Shops, community, licenses, users: Mongo collections with indexes on lookup keys.
  • Health: lib/db-health pings Mongo separately from Neon and Sanity.
  • If the document is copy editors will change weekly — consider Sanity instead.

Related

Part of the ReactBD MERN documentation. Static content, rendered on the server.