I am trying to create a backend for a learning project, because of learning purposes I would like to complete this project without using Mongoose. Currently I initiated MongoDB in a db.js file under config folder. Within server.js I can succesfully call the function and wait for a promise before listening on the defined port with Express.
Now within the Controller for the specific collection I have to call the same function (connectToDb) again to succesfully create, find, update etc.
I was wondering if this is the actual way it should be done, isn't there like a functionality to just do CRUD actions on the collection within the controllers without using the connectToDb functionality again. What it looks like to me is that within server.js you already start the connection and for every action I have to create another connection and something does not feel right this way.
For example purposes I have only included createHabitCard, also I know that this is not the way a controller is used and that a lot of functionality belongs in a service file but this is because I am still learning and slowly expanding "my creation".
I hope someone can give me some advice on this, thanks in advance :)
Server.js
require("dotenv").config({ path: "./config.env" });
const express = require("express");
const connectToDb = require("./configs/db.config");
const cors = require("cors");
const path = require("path");
const habitcardRouter = require("./routes/habitcardRoutes");
const app = express();
const PORT = process.env.PORT || 5000;
// Middleware
app.use(cors());
app.use(express.json());
app.use("/", express.static(path.join(__dirname, "/public")));
//First routes
app.use("/api/habitcards", habitcardRouter);
let db;
connectToDb()
.then((res) => {
app.listen(PORT, () => {
console.log(`Server is running on port: ${PORT}`);
});
})
.catch((err) => {
console.log(err);
});
module.exports = { db };
db.config.js
const { MongoClient } = require("mongodb");
const uri = process.env.ATLAS_URI;
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const connectToDb = async () => {
try {
const database = client.db("howwasmyday");
return database;
} finally {
await client.close();
}
};
module.exports = connectToDb;
habitcardController.js
const connectToDb = require("../configs/db.config");
//Create habitcard
const createHabitCard = async (req, res) => {
const habitcard = req.body;
try {
let db = await connectToDb();
const collection = db.collection("habitcards");
collection.insertOne(habitcard, (err, result) => {
res.json("It all worked");
});
} catch (err) {
console.log(err);
}
};
module.exports = {
createHabitCard,
};
Right, you don't need to create a new connection for every controller and request. Change your db.config.js
file in the following way:
const {MongoClient} = require("mongodb");
const uri = process.env.ATLAS_URI;
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true
});
let database;
const connectToDb = async () => {
if (database) {
return database;
}
try {
database = client.db("howwasmyday");
return database;
} finally {
await client.close();
}
};
module.exports = connectToDb;
This pattern is called Singleton. Every time you call connectToDb
, it will return already cached instance of database connection and avoid connecting to it again.
This works in node.js, b/c every time you require db.config.js
from different files, it will cache the module, so you'll have db connection persisting in memory.
You can read more about this here.
Modules are cached after the first time they are loaded. This means (among other things) that every call to require('foo') will get exactly the same object returned, if it would resolve to the same file.