I'm trying send data from a MongoDB server to the client on a GET request. I'm using an express server and want to send all the documents in a specific collection when a GET request is received.
I know MongoDB is asynchronous, so I query an asynchronous function with a promise. I am able to log to console all the documents in the MongoDB Collection but when I return it the data becomes undefined. I have a database called 'testDB' and I want to return all the documents in a specific collection ('testCollection').
app.get('/getData', (req, res) => {
returnData().then(result => {
console.log(result); //This logs undefined
res.send(result); //This sends undefined
})
});
async function returnData() {
const uri = "mongodb+srv://" + "username" + ":" + "password" + "#" + "connection url" + "/?retryWrites=true&w=majority";
//Connect to the database and return all documents in the collection
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true
});
try {
await client.connect();
const database = client.db('testDB');
const collection = database.collection('testCollection');
const query = {};
const options = {};
const cursor = collection.find(query, options);
await cursor.toArray().then((docs) => {
console.log(docs); // <- This works and logs all the data to console
return docs;
});
} catch (e) {
console.error(e);
} finally {
await client.close();
}
}
EDIT:
I tried this and it returns undefined.
try {
await client.connect();
const database = client.db('testDB');
const collection = database.collection('testCollection');
const query = {};
const options = {};
const cursor = collection.find(query, options);
await cursor.toArray().then((docs) => {
return cursor.toArray();
});
} catch (e) {
console.error(e);
} finally {
await client.close();
}
I tried this and I get [MongoPoolClosedError]: Attempted to check out a connection from closed connection pool
try {
await client.connect();
const database = client.db('testDB');
const collection = database.collection('testCollection');
const query = {};
const options = {};
const cursor = collection.find(query, options);
return cursor.toArray();
} catch (e) {
console.error(e);
} finally {
await client.close();
}
Don't open and close the mongo client on each GET. Connect once and export that connection to your other modules. In terms of the Express request/response. You hand it off something like below.
app.get('/getData', myAsyncFunction)
async function index (req, res) {
let r = await db.collection("testDB").find(query).toArray();
return res.send(r);
}
Related
I have Node.js express app with Postgres as a database. I'm using pg for database communication from the app.
This is how my db.service looks like
import { Pool } from 'pg';
const dbConfig = {/*my valid db configuration*/};
const pool = new Pool(dbConfig);
export const connectDB = async () => {
let client;
try {
client = await pool.connect();
} catch (error) {
console.error('error while connecting to database', error);
process.exit(1);
}
return client;
};
I have two queries as below
#1.
export const fetchUser = async (email) => {
const client = await connectDB();
const query = `
SELECT full_name FROM app.users
WHERE email = '${email}'
`;
let result;
try {
result = await client.query(query);
if (result.rowCount) {
return result.rows[0].full_name;
}
} catch (error) {
} finally {
await client.release();
}
return result;
};
#2
export const fetchWallet = async (email) => {
const client = await connectDB();
const query = `
SELECT wallet_money FROM app.user_wallet
WHERE email = '${email}'
`;
let result;
try {
result = await client.query(query);
if (result.rowCount) {
return result.rows[0].wallet_money;
}
} catch (error) {
} finally {
await client.release();
}
return result;
};
Now from one of my controller.js if I call these function separate await, no issues
ctrl.js
const fullName = await fetchUser('some#gmail.com');
const walletMoney = await fetchWallet('some#gmail.com');
No issues this way, however if I merge them into a single promise
const $0= fetchUser('some#gmail.com');
const $1= fetchWallet('some#gmail.com');
const result = await Promise.all([$0, $1]);
this throws the below error
error while connecting to database Error: timeout exceeded when trying
to connect at Error
Please suggest why this error is popping up & how can I get rid of it?
Thanks!
That's happening because you are trying to connect to DB separately for every query.Try to create one connection and use it for all queries!
I'm staggered by how much I'm struggling to delete a document in MongoDB from Express JS. I'm finding the mix of documentation and methods including deleteOne, findByIdAndRemove, some tutorials say you need to declare an ObjectId, and some don't. Madness.
Anyway, below is my code. I have a function to connect to the database:
const withDB = async (operations, res) => {
try {
const client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
const db = client.db('database-name');
await operations(db);
client.close();
} catch (error) {
res.status(500).json({ message: 'Error connecting to db', error });
}
}
And then the below is my delete command:
app.delete('/api/reports/delete-report/:id', async (req, res) => {
//call withDB function above
withDB(async (db) => {
//delete command
const result = await db.collection('reports').deleteOne( { _id : new MongoClient.ObjectId(req.params.id) } );
//get reports
const reportInfo = await db.collection('reports').find().toArray()
//put returned reports into the result provided
res.status(200).json(reportInfo);
}, res);
});
For my troubles I get the message 'Error connecting to db'. If I make the delete command:
const result = await db.collection('reports').deleteOne( { _id : req.params.id } );
I just get the contents of the database returned, but there's no deletion.
The issue is:
new MongoClient.ObjectId(req.params.id)
You don't want to create another mongoclient. It needs to be
new ObjectId(req.params.id)
and make sure you import that class:
const { MongoClient, ObjectId } = require('mongodb');
I am following MongoDB Atlas Blog tutorial to add info into mongodb but I am getting the above mentioned error. I have tried to resolve this error, even tried to then but still getting the same issue...
Following is my connection file
async function main(){
/**
* Connection URI. Update <username>, <password>, and <your-cluster-url> to reflect your cluster.
* See https://docs.mongodb.com/ecosystem/drivers/node/ for more details
*/
const uri = "mongodb+srv://an:abc#abc-2yzxs.mongodb.net/test?retryWrites=true&w=majority";
const client = new MongoClient(uri);
try {
// Connect to the MongoDB cluster
await client.connect();
await createListing(client,
{
msg: client.msg
}
);
return client;
} catch (e) {
console.error(e);
} finally {
await client.close();
}
}
main().catch(console.error);
async function createListing(client, newListing){
const result = await client.db("mydb").collection("mycollection").insertOne(newListing);
console.log(`New listing created with the following id: ${result.insertedId}`);
}
and following is my schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const mySchema = new Schema(
{
msg: {
type: String
}
}
);
let a = mongoose.model("mycollection", mySchema);
module.exports = a;
My Controller:
const Log = require('../models/mySchema');
require('../connection');
function createListing(data){
let Log = new Log({ msg: data});
var err = Log.save();
console.log("err is : ", err)
}
exports.createListing = createListing;
this is how I'm calling from server file
let log = require('./controllers/myController');
log.createListing(data);
Personally I wouldn't use mongoose, although you tutorial does. I just use mongodb.
import { connect } from 'mongodb'
async function main() {
// try-catch
const MONGO = 'mongodb+srv://an:abc#abc-2yzxs.mongodb.net/testretryWrites=true&w=majority'
const client = await connect(MONGO, {
useNewUrlParser: true,
useUnifiedTopology: true })
const mongo = client.db()
const Log = mongo.collection('Log')
await Log.insertOne({ message: 'test' })
}
I know it's different from the problem you have but I just don't know why mongoose is needed. It's not really.
you need to use await before Log.save() to log actual value returning by the function instead of promise pending you are getting.
async function createListing(data){
let Log = new Log({ msg: data});
let err = await Log.save();
console.log("err is : ", err)
}
I have a express server that handles queries on a SQL Server server database. For my logs, I am logging all requests that are received into a logs table:
const morgan = require('morgan')
const stream = require('stream')
const DatabaseController = require('./dbcontroller')
const databaseStream = new stream.Writable()
// anything that is logged is passed to this stream... the text is logged
databaseStream.write = async (text) => {
await DatabaseController.addLog({ text })
}
// remove the img binary from the body - which is too large
const logger = morgan( 'tiny', { immediate: true, stream: databaseStream })
module.exports = logger
Additionally, there are some requests that can be made that will also make connections on the database.
router.get('/logs', async (req, res, next) => {
try {
const logdata = await DatabaseController.getLogs()
res.status(200)
res.send({ logdata })
} catch (err) { next(err) }
})
Normal requests are fine. However anytime I make a request that requires connecting to the database (GET/logs). I get the following:
Global connection already exists. Call sql.close() first.
DatabaseController implementation:
const sql = require('mssql')
const config = require('config')
const dconfig = config.database
const addLog = async (options) => {
const { text, isError } = options
try {
let pool = await sql.connect(dconfig)
let request = await pool.request()
request.input('message', sql.Text, text.trim())
if (isError) {
request.input('iserr', sql.Bit, isError)
await request.query('insert into dbo.MApp_Logs (message, is_error) values (#message, #iserr)')
} else {
await request.query('insert into dbo.MApp_Logs (message) values (#message)')
}
} catch (err) {
throw err
} finally {
await sql.close()
}
}
const getLogs = async () => {
try {
let pool = await sql.connect(dconfig)
return await pool.request().query('SELECT * FROM dbo.MApp_Logs')
} catch (err) {
throw err
} finally {
await sql.close()
}
}
The issue is that as is. I can't prevent my code from attempting to make multiple connections. Using a connection pool gives me the following error:
"message": "Already connecting to database! Call close before connecting to different database.",
"code": "EALREADYCONNECTING"
How can I get around this issue? Is it possible to wait for a connection to close or use the existing connection?
I've tried to find a solution to this question in: http://mongodb.github.io/node-mongodb-native/
However, I could not find a solution to listing all available MongoDB databases from a Node.js app.
Use db.admin().listDatabases.
You can do this now with the Node Mongo driver (tested with 3.5)
const MongoClient = require("mongodb").MongoClient;
const url = "mongodb://localhost:27017/";
const client = new MongoClient(url, { useUnifiedTopology: true }); // useUnifiedTopology removes a warning
// Connect
client
.connect()
.then(client =>
client
.db()
.admin()
.listDatabases() // Returns a promise that will resolve to the list of databases
)
.then(dbs => {
console.log("Mongo databases", dbs);
})
.finally(() => client.close()); // Closing after getting the data
Only admins can see all the database. So connect to mongodb database with admin creds then create an admin instance by await db.admin(), then list down all databases await adminDB.listDatabases()
const MongoClient = require('mongodb').MongoClient;
let client = await MongoClient.connect(process.env.MONGO_DB_URL);
const db = await client.db(process.env.DEFAULT_DB_NAME);
let adminDB = await db.admin();
console.log(await adminDB.listDatabases());
If you are using mongoose, the below code can be used.
import mongoose from 'mongoose';
mongoose.set('strictQuery', true);
mongoose.connect('mongodb://localhost:27017/mydb', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const db = mongoose.connection;
// Check DB Connection
db.once('open', () => {
(async () => {
const data = await mongoose.connection.db.admin().command({
listDatabases: 1,
});
console.log(data);
})();
console.log('Connected to MongoDB');
});
// Check for DB errors
db.on('error', (err) => {
console.log('DB Connection errors', err);
});
export default mongoose;
If you want to get the database list on your other functions, make sure the connection is established first and also make sure the user has admin access and then just do the below query. This is a sample from my API router.
// Get all databases
router.get('/database/get', async (req, res) => {
try {
const data = await mongoose.connection.db.admin().command({
listDatabases: 1,
});
if (data && data !== null) {
res.status(200).send({ data: data });
return;
}
res.status(200).send({ data: null, message: 'Data not found' });
} catch (e) {
// eslint-disable-next-line no-console
console.log(e);
res.status(500).send(e.message);
}
});
*Its difficult to get list by db.admin().listDatabases, below code will work fine in nodejs *
const { promisify } = require('util');
const exec = promisify(require('child_process').exec)
async function test() {
var res = await exec('mongo --eval "db.adminCommand( { listDatabases: 1 }
)" --quiet')
return { res }
}
test()
.then(resp => {
console.log('All dbs', JSON.parse(resp.res.stdout).databases)
})
test()