(node:21140) UnhandledPromiseRejectionWarning: MongoError: Topology is closed, please connect
I think it is an error relating to repetition of client.connect and client.close in each function of CRUD operation as shown in code below
//I think there is no issue with cluster uri so I didn't post that since it connects fine
const MongoClient = require("mongodb").MongoClient;
const uri = "mongodb+srv://username:pasword@some cluster";
//I think there is no issue with cluster uri so I didn't post that since it connects fine
const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true });
async function AddnewService(service) {
await client.connect()
const database = client.db("twitch")
const usr = await database.collection("recipes").find({ Title: service.Title }).toArray()
if (usr.length >= 1) {
await client.close()
return { message: "Title already in use" }
}
else {
const result = await database.collection("recipes").insertOne(service)
await client.close()
return { message: "Service Added to Database" }
}
}
async function FetchAllserviceSearch(service) {
await client.connect()
const database = client.db("twitch")
let recipesCursor = await database.collection("recipes").find({})
while (await recipesCursor.hasNext()) { //make sure to put await on cursor .hasnext()
let recipe = await recipesCursor.next()
console.log(recipe)
}
await client.close()
return { message: "User Added to Database" }
}
module.exports = {
AddnewService,
FetchAllserviceSearch
}
I tried allot to make that client.connect and client.close be called only once but there have always be a new error caused by it. I cannot eliminate module.export cause I need them for usage in other file.
So I need a little help to make it work out some how without that needless repetition as mongodbAtlas causes issues with that.
Don't close your DB connection, you're in race condition, when two requests come one will close other will open.
client.close(); -----> remove
Use the same db connection for all the requests.
const uri = "mongodb+srv://username:pasword@cluster0.ik1pu.mongodb.net/myFirstDatabase?retryWrites=true&w=majority";
let con;
async function connect(service) {
if (con) return con; // return connection if already conncted
const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true });
con = client.connect()
return con;
}
async function AddnewService(service) {
const client = await connect();
const database = client.db("twitch")
const usr = await database.collection("recipes").find({ Title: service.Title }).toArray()
if (usr.length >= 1) {
// await client.close(); -----> remove
return { message: "Title already in use" }
}
else {
const result = await database.collection("recipes").insertOne(service)
return { message: "Service Added to Database" }
}
}
async function FetchAllserviceSearch(service) {
const client = await connect();
const database = client.db("twitch")
let recipesCursor = await database.collection("recipes").find({})
while (await recipesCursor.hasNext()) { //make sure to put await on cursor .hasnext()
let recipe = await recipesCursor.next()
console.log(recipe)
}
// await client.close(); -----> remove
return { message: "User Added to Database" }
}
module.exports = {
AddnewService,
FetchAllserviceSearch
}
You are trying to close the client in a request handler, but your client is global.
If you want to have a global client, do not close it in request handlers.
If you want to close the client in request handlers, create the client in the handler of the same request.
Create a Connection class to manage the apps database connection.
MongoClient does not provide a singleton connection pool so you don't want to call MongoClient.connect() repeatedly in your app. A singleton class to wrap the mongo client works for most apps I've seen.
const MongoClient = require('mongodb').MongoClient
class Connection {
static async open() {
if (this.db) return this.db
this.db = await MongoClient.connect(this.url, this.options)
return this.db
}
}
Connection.db = null
Connection.url = 'mongodb://127.0.0.1:27017/test_db'
Connection.options = {
bufferMaxEntries: 0,
reconnectTries: 5000,
useNewUrlParser: true,
useUnifiedTopology: true,
}
module.exports = { Connection }
Everywhere you require('./Connection'), the Connection.connectToMongo() method will be available, as will the Connection.db property if it has been initialised.
const { Connection } = require('../lib/Connection.js')
async function AddnewService(service) {
// This should go in the app/server setup, and waited for.
await Connection.connectToMongo()
const database = Connection.db("twitch")
const usr = await database.collection("recipes").find({ Title: service.Title }).toArray()
if (usr.length >= 1) {
// await client.close()
return { message: "Title already in use" }
}
else {
const result = await database.collection("recipes").insertOne(service)
// await client.close()
return { message: "Service Added to Database" }
}
}
In express you can do something like this,
import {MongoClient} from 'mongodb';
import express from 'express';
import bodyParser from 'body-parser';
let mongoClient = null;
MongoClient.connect(config.mongoURL, {useNewUrlParser: true, useUnifiedTopology: true},function (err, client) {
if(err) {
console.log('Mongo connection error');
} else {
console.log('Connected to mongo DB');
mongoClient = client;
}
})
let app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use((req,res,next)=>{
req.db = mongoClient.db('customer_support');
next();
});
And use in your request handles like this,
router.post('/hello',async (req,res,next)=>{
let uname = req.body.username;
let userDetails = await AddnewService(req.db,uname) // change signature of this method
res.statusCode = 200;
res.data = userDetails;
next();
});