Source: lib/mongodb.js

// This approach is taken from https://github.com/vercel/next.js/tree/canary/examples/with-mongodb
import { MongoClient } from "mongodb";
import { backOff } from "exponential-backoff";

if (!process.env.MONGODB_DB) {
  throw new Error("Please add your Mongo DB Name to .env");
}

if (!process.env.MONGODB_URI) {
  throw new Error("Please add your Mongo URI to .env");
}

const uri = process.env.MONGODB_URI;
const options = {
  useUnifiedTopology: true,
  useNewUrlParser: true,
  dbName: process.env.MONGODB_DB,
};

let client;
let clientPromise;

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();
}

async function _connectToDatabase() {
  const client = await getMongoClient();
  await client.connect();
  const db = client.db(process.env.MONGODB_DB);

  return db;
}

export async function connectToDatabase() {
  return await backOff(() => _connectToDatabase(), {
    numOfAttempts: 10,
    jitter: "full",
    retry: (error, attemptNumber) => {
      console.warn(`Retrying connection to database #${attemptNumber}`, error);
      return true;
    },
  });
}

export async function getMongoClient() {
  const client = await clientPromise;

  if (!client) {
    throw new Error("Not connected to database");
  }

  return client;
}

export async function getDreamsCollection() {
  return (await connectToDatabase()).collection("dreams");
}

export async function getCommentsCollection() {
  return (await connectToDatabase()).collection("comments");
}

export async function getStarsCollection() {
  return (await connectToDatabase()).collection("stars");
}

export async function getInboxCollection() {
  return (await connectToDatabase()).collection("inbox");
}

export async function getCompletionsCollection() {
  return (await connectToDatabase()).collection("completions");
}

/**
 * This collection is automatically generated by next-auth
 */
export async function getUsersCollection() {
  return (await connectToDatabase()).collection("users");
}

/**
 * This collection is automatically generated by next-auth
 */
export async function getAccountsCollection() {
  return (await connectToDatabase()).collection("accounts");
}

// Export a module-scoped MongoClient promise. By doing this in a
// separate module, the client can be shared across functions.
export default clientPromise;
export { clientPromise as clientPromise };