MONGODB


Web Development

Updated Jun 5th, 2022

Overview

NoSQL document database. Atlas is the Cloud, Compass is the local GUI. Installing the shell is probably worth it. Apparently upgrading your MongoDB or updating it is not the easiest task?

So Many Drivers/Versions

The docs are bit of a mess because there are many drivers for different languages and versions for each of those. I’m typically looking at the Node driver with latest docs here.

MongoID

If you are getting weird errors or queries are not returning results as expected it may be related to this. For example, you may need to run a map to run the “toString” on any of these mongo-generated unique ids. And when trying to match an id that is a simple string to an id you need to import and create a new instance of that MongoId. For example:

let pets = await petsCollection.find({ petId: new ObjectId(sessionId) }).toArray()

Oh yeah and “toArray” resolves the promise and returns the result as an array.

Can you customize the name of the _id property?

Here is a stack overflow about customizing the value as long as it meets certain restrictions. The Mongo docs here explain that: “The field name _id is reserved for use as a primary key; its value must be unique in the collection, is immutable, and may be of any type other than an array.” So no.

Query Versus Aggregate Operations

Aggregate is faster, sends less data over the wire but you have to learn the syntax.

Aggregate Operations

I need to dig deeper into when this is necessary. Seems powerful. Also fairly confusing, mostly because the docs are a fire hose of too much information.

For example. Given a username and I need to look up an id and then use that id to find items in a different collection I could do a await a “findOne” and then a “find.”

Or can you use aggregate operations. Something like:

let cars = await usersCollection
        .aggregate([
          // look up id from users given the username
          { $match: { username: username } },
          { $lookup: { from: "cars", localField: "_id", foreignField: "authorId", as: "carDocument" } },
          { $unwind: "$carDocument" },
          {
            $project: {
              description: 1,
              createdDate: 1,
              username: 1,
              // kind of merge the two together
              cars: {
                description: "$carDocument.description",
                price: "$carDocument.price",
                miles: "$carDocument.miles",
                link: "$carDocument.link"
              }
            }
          }
        ])
        .toArray()

To Mongoose or Not Mongoose?

As of now I like rolling without it.

Connection Pooling With NextJS

A good place to start is MongoDB’s intro to pooling here and Vercel’s post on databases here.

And mongoDB article here (outdated).

The key to remember is that NextJS is a serverless environment. This means you need to open and close any connection, which results in a lot of connections, so you will want to take advantage of “connection pooling.”

The Next docs say you may reach for PgBouncer for this.

Look to the Example With Mongo DB

Link here

Article from MongoDb here where they discuss this example “follows MongoDB best practices for connectivity, connection pool monitoring, and querying.”

Node Driver Docs here

The magic:

import { MongoClient } from 'mongodb'
const uri = process.env.MONGODB_URI
const options = {}
let client
let clientPromise
if (!process.env.MONGODB_URI) {
  throw new Error('Please add your Mongo URI to .env.local')
}
if (process.env.NODE_ENV === 'development') {
  // In development mode, use a global variable so that the value
  // is preserved across module reloads caused by HMR (Hot Module Replacement).
  if (!global._mongoClientPromise) {
    client = new MongoClient(uri, options)
    global._mongoClientPromise = client.connect()
  }
  clientPromise = global._mongoClientPromise
} else {
  // In production mode, it's best to not use a global variable.
  client = new MongoClient(uri, options)
  clientPromise = client.connect()
}
// Export a module-scoped MongoClient promise. By doing this in a
// separate module, the client can be shared across functions.
export default clientPromise

Note: next start and next build will set NODE_ENV to production by default, but if there’s a custom NODE_ENV it won’t be replaced.

Caching

Vercel docs on caching here.

The mongodb article from the previous section is outdated and references a utility file that is no longer around.

This is an alternative setup with caching here.

Pagination

This solves the issue. Server side render a reasonable amount and ping API for more using “skip” and “limit”

A good tutorial here

Scaling MongoDb

Is the number of simultaneous connections a thing? What is the slowing and breaking point?