Get results with Mongodb Nodejs driver and Express using find - node.js

I have a tiny express server that I want to use to get some data from a collection in my database:
const express = require('express');
const MongoClient = require('mongodb').MongoClient;
const app = express();
const PORT = 3000;
const MONGO_URI = 'mongodb://127.0.0.1:27017/test';
async function myReport(schoolId) {
const client = new MongoClient(MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true });
try {
await client.connect()
console.log("Hello!"); // This is never ran unless I remove "await" from the above line :S
const db = client.db();
const result = db.collection('states').find({}).map((a, b, c) => {
console.log("This never runs", a, b, c);
return "asdf";
});
return result;
} catch (err) {
console.log("ERROR", err);
}
client.close();
};
// Hoisting server
app.get('/api/reports/states/:id', async function (req, res, next) {
const report = myReport(req.params.id)
res.json(report); // {}
});
app.listen(PORT, (err) => {
console.log(`reporting listening in`, PORT);
});
I really don't know what I'm doing wrong here. Tried using .each, toArray and I'm not able to get the actual results as a list.
I've been following these docs: https://mongodb.github.io/node-mongodb-native/3.6/api/Cursor.html
Any idea what I'm doing wrong?

As per your saying:
await client.connect()
console.log("Hello!"); // This is never ran unless I remove "await" from the above line :S
I think connection is not establishing. I tried your code with a little bit modification. I created a cluster on Atlas Mongodb and used its URI as a connection string.
const express = require('express');
const MongoClient = require('mongodb').MongoClient;
const app = express();
const PORT = 3000;
const MONGO_URI = 'mongodb+srv://<username>:<password>#cluster0-oqotc.mongodb.net/<dbname>?retryWrites=true&w=majority';
const getListings = async () => {
const client = new MongoClient(MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true });
try {
await client.connect()
console.log("Hello!"); // This will print now :-)
const listings = await client.db("sample_airbnb").collection("listingsAndReviews").findOne({});
return listings;
} catch (err) {
console.log("ERROR", err);
}
client.close();
};
// Hoisting server
app.get('/api/get-listings', async function (req, res, next) {
const report = await getListings()
res.json(report);
});
app.listen(PORT, (err) => {
console.log(`reporting listening in`, PORT);
});
You need to change username, password and dbname with your ones.
Note: While using Atlas Mongodb Cluster, if you are getting connection error, you need to whitelist your ip as well.
Hope it will help you. Thanks

You defined myReport as an asynchronous function which returns a promise. Add toArray() back into your code and then get your report like this
app.get('/api/reports/states/:id', async function (req, res, next) {
myReport(req.params.id).then(report => {
res.json(report);
});
});
Because its the call to res.json is also in an asynchronous function I think you can also do
app.get('/api/reports/states/:id', async function (req, res, next) {
const report = await myReport(req.params.id);
res.json(report);
});

Related

Cannot read property 'db' of undefined after defining MongoClient.connect() outside of app.js

Previously I had a working, single-file, application that was essentially:
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const io = require('socket.io')(server);
app.set('socketio', io);
import { MongoClient } from 'mongodb';
var mongo_client;
var connection_string = 'mongodb://localhost:27017' // for local development environment
MongoClient.connect(connection_string, (err, client) => {
if (err) {
throw err;
} else {
mongo_client = client;
}
});
/*
lots of middleware and route handling
*/
server.listen(PORT, () => {
console.log(`running on port ${PORT}`);
});
In middleware, within the same file, I would make a call to the database with something like:
var collection = mongo_client.db('users').collection('users');
var query = { };
var options = { };
try {
var user = await collection.findOne(query, options);
return user;
} catch (err) {
console.log('error: ' + err);
}
I'm in the process of refactoring and have put the MongoClient.connect() block in its own file.
config/database.js now looks like:
const MongoClient = require('mongodb').MongoClient;
const { connection_string } = require('./environment_variables');
let mongo_client;
MongoClient.connect(connection_string, (err, client) => {
if (err) {
throw err;
} else {
mongo_client = client;
console.log('FIRST: mongo_client: ');
console.log(mongo_client);
}
});
console.log('SECOND: mongo_client: ');
console.log(mongo_client);
module.exports = mongo_client;
To begin testing how to import it into another file, I am doing this in app.js:
const mongo_client = require('./config/database');
app.get('/', async (req, res) => {
const query = { username: 'my name' };
const collection = mongo_client.db('users').collection('users'); // <----- error is here, mongo_client is undefined
const result = await collection.findOne(query);
res.json({ result: result });
});
(note, when I better understand how all this works, I will move the route and middleware handling out of app.js)
The console logs are:
SECOND: mongo_client:
undefined
FIRST: mongo_client:
MongoClient {
// ... lots of mongodb client properties here
}
UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'db' of undefined
So it seems like MongoClient.connect() is asynchronous and therefore mongo_client is not defined before I try and call mongo_client.db('users').collection('users').
My questions are:
How can this issue be resolved?
Why wasn't it happening before I extracted MongoClient.connect() out of app.js?
I've been reading articles like these:
MongoClient connection pooling
MongoClient docs
But they seem to either:
Include subsequent calls to the database within the MongoClient.connect() function block (and i can't wrap my whole app within that function) OR
They call app.listen() within the MongoClient.connect() function block (and this is also not feasible as I do several things with express() and http and socket.io)
Environment:
node v14.18.1
"mongodb": "^4.1.4" (native driver)
Edit:
I tried the following changes in config/database.js per this article.
I replaced this:
const MongoClient = require('mongodb').MongoClient;
const { connection_string } = require('./environment_variables');
let mongo_client;
MongoClient.connect(connection_string, (err, client) => {
if (err) {
throw err;
} else {
mongo_client = client;
}
});
module.exports = mongo_client;
with this:
const MongoClient = require('mongodb').MongoClient;
const { connection_string } = require('./environment_variables');
const mongo_client = new MongoClient(connection_string);
const is_this_the_right_thing_to_do = async () => {
await mongo_client.connect();
};
is_this_the_right_thing_to_do();
module.exports = mongo_client;
The error went away and it is returning a result, but I am not sure if this is the correct way to do it.
Also, I am not sure how I can use the single instance of mongo_client in all routes and middleware. I tried requiring it at the top of app.js, hoping that the routes and middleware (in other files) that were required afterwards would have access to it, but they don't, this was the error in the route middleware:
UnhandledPromiseRejectionWarning: ReferenceError: mongo_client is not defined
Another edit:
I've posted an answer on someone else's question here which may be what I am looking for, will update this post after further testing.
If you are trying to access database (mongo_client.db("your database name here") in your case) from other routes then you can maybe try something like this in your app.js or index.js.
const connectDB = async () => {
await client.connect();
db = client.db("greendeckData");
app.use((req, res, next) => {
req.db = db;
next();
});
app.use("/get", getRouter);
};
You can check the source from where I found the solution link, here

Express can't start the server or connect to MongoDB

I'm a beginner and try to create a rest API following this tutorial. I expected to see Server is running on port: ${PORT}, but it seems like my code can't reach it. I got no error on my terminal and it looks like this
Here are my code:
server.js
require('dotenv').config({ path: './config.env' });
const express = require('express');
const cors = require('cors');
const dbo = require('./db/conn');
const PORT = process.env.PORT || 5000;
const app = express();
app.use(cors());
app.use(express.json());
app.use(require('./api/api'));
// Global error handling
app.use(function (err, _req, res) {
console.error(err.stack);
res.status(500).send('Something broke!');
});
// perform a database connection when the server starts
dbo.connectToServer(function (err) {
if (err) {
console.error(err);
process.exit();
}
// start the Express server
app.listen(PORT, () => {
console.log(`Server is running on port: ${PORT}`);
});
});
conn.js
const MongoClient = require('mongodb').MongoClient
const dotenv = require("dotenv")
dotenv.config()
const connectionString = process.env.MONGOURI
let db;
module.exports = {
connectToServer : function(callback) {
MongoClient.connect(connectionString, {
useUnifiedTopology: true
}, (err, client) => {
if (err) return console.error(err)
db = client.db('db-name');
console.log('Connected to Database');
return callback
});
},
getDb: function () {
return db;
}
}
api.js
const express = require("express");
const gameRoutes = express.Router();
const dbo = require('../db/conn');
gameRoutes.route("/game").get(async function (_req, res) {
const dbConnect = dbo.getDb();
dbConnect
.collection("game")
.find({}).limit(50)
.toArray(function(err, result) {
if (err) {
res.status(400).send("Error fetching listings!");
} else {
res.json(result);
}
})
})
module.exports = gameRoutes;
Can you please tell me what's wrong with my code? I really can't find why the server is not running. Thanks in advance! I'll be very grateful for your help!
In your connectToServer method you just returning the callback. But you actually need to call it as well.
So change this
return callback
to this
return callback(null);
If you want to pass the possible error from MongoClient to the callback as well, then change your connectToServer method to this :
connectToServer : function(callback) {
MongoClient.connect(connectionString, {
useUnifiedTopology: true
}, (err, client) => {
if (err) { return callback(err); }
db = client.db('db-name');
console.log('Connected to Database');
return callback(null) // no error, so pass "null"
});
}

When I click the client site then show this error

This is my Code
const express = require('express');
const { MongoClient } = require('mongodb');
const cors = require('cors');
require('dotenv').config()
const app = express();
const port = 5000;
// middle ware
app.use(cors());
app.use(express.json());
const uri = `mongodb+srv://${process.env.DB_USER}:${process.env.DB_PASS}#cluster0.84pml.mongodb.net/myFirstDatabase?retryWrites=true&w=majority`;
const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true });
client.connect(err => {
const collection = client.db("carMechanic").collection("services");
// perform actions on the collection object
client.close();
});
async function run() {
try {
await client.connect();
const database = client.db("carMechanic");
const servicesCollection = database.collection("services");
// post API
app.post('/services', async (req, res) => {
const service = req.body;
// console.log('hit the post is', service);
const result = await servicesCollection.insertOne(service);
console.log(result);
res.send('post hitted')
});
}
finally {
// await client.close();
}
}
run().catch(console.dir);
app.get('/', (req, res) => {
res.send('Running Genius Server');
});
app.listen(port, () => {
});
And This is the error message
G:\web_projects\practice\node\module-65-Genius-car\backend\node_modules\mongodb\lib\utils.js:690
throw new error_1.MongoRuntimeError(`illegal state transition from [${target.s.state}] => [${newState}], allowed: [${legalStates}]`);
^
MongoRuntimeError: illegal state transition from [closed] => [connected], allowed: [closed,connecting]
at stateTransition (G:\web_projects\practice\node\module-65-Genius-car\backend\node_modules\mongodb\lib\utils.js:690:19)
at G:\web_projects\practice\node\module-65-Genius-car\backend\node_modules\mongodb\lib\sdam\topology.js:226:21
at G:\web_projects\practice\node\module-65-Genius-car\backend\node_modules\mongodb\lib\cmap\connection_pool.js:272:25
at handleOperationResult (G:\web_projects\practice\node\module-65-Genius-car\backend\node_modules\mongodb\lib\sdam\server.js:363:9)
at MessageStream.messageHandler (G:\web_projects\practice\node\module-65-Genius-car\backend\node_modules\mongodb\lib\cmap\connection.js:479:9)
at MessageStream.emit (events.js:375:28)
at processIncomingData (G:\web_projects\practice\node\module-65-Genius-car\backend\node_modules\mongodb\lib\cmap\message_stream.js:108:16)
at MessageStream._write (G:\web_projects\practice\node\module-65-Genius-car\backend\node_modules\mongodb\lib\cmap\message_stream.js:28:9)
at writeOrBuffer (internal/streams/writable.js:358:12)
at MessageStream.Writable.write (internal/streams/writable.js:303:10)
The problem is this line:
client.connect(err => {
const collection = client.db("carMechanic").collection("services");
// perform actions on the collection object
client.close();
});
you don't need this because you made connection to the database later
I answered myself.
I had made a mistake, that was I wrote the extra code given below.
client.connect(err => {
const collection = client.db("carMechanic").collection("services");
// perform actions on the collection object
client.close();
});
Because this has already been declared inside of my function.
await client.connect();
const database = client.db("carMechanic");
const servicesCollection = database.collection("services");
When I remove this--
client.connect(err => {
const collection = client.db("carMechanic").collection("services");
// perform actions on the collection object
client.close();
});
the code is working well.
I have my code below and works as is. A few things:
Install dependencies again if needed
I am using the sample airbnb db they provide in Atlas service on cloud.mongodb.com
Change username:0987654321 to your own username and password where 'username' is the username and 0987654321 is the password.
Run() works fine locally but it's not straight forward to have a local method start a server endpoint and keep track of it or vice versa. For that you need good chaining
So only USE the run method to test locally and it works fine or use the express endpoints to test via your app client side or curl or postman and that works too independently.
Heres my whole server file:
const express = require('express');
const { MongoClient } = require('mongodb');
const cors = require('cors');
// middle ware
const app = express();
const port = 5000;
// middle ware
app.use(cors());
app.use(express.json());
const uri = "mongodb://username:0987654321#cluster0-shard-00-00.czraf.mongodb.net:27017,cluster0-shard-00-01.czraf.mongodb.net:27017,cluster0-shard-00-02.czraf.mongodb.net:27017/sample_airbnb?ssl=true&replicaSet=atlas-wmfxrm-shard-0&authSource=admin&retryWrites=true&w=majority";
async function run(){
//1 connect
const client = await MongoClient.connect(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
//2 set db and collection
const collection = client.db("sample_airbnb").collection("listingsAndReviews");
// perform actions on the collection object
//3 CREATE
let doc = {
"_id":"100009090",
"listing_url":"https://www.airbnb.com/rooms/100009190",
"name":"Ribeira Charming Duplex",
"summary":"Fantastic duplex apartment with three bedrooms, located in the historic area of Porto, Ribeira (Cube)",
"neighborhood_overview":"In the neighborhood of the river, you can",
"price":"250gbp"
}
//4.0 POST INDEPENDENTLY
//collection.insertOne(doc).then(doc => { console.log('inserted id is: ', doc.insertedId)});
//FIND ONE
//const result = await collection.find( {_id: {$eq: "10006546"} }).toArray();
//FIND MANY
//const many = await collection.find( {_id: { $in: ["10006546", "100009090"] } } ).toArray();
//LOGGER
//console.log(many)
client.close();
};
//server methods
//get route
app.get('/', (req, res) => {
res.send('Running Genius Server');
});
//get docs
app.get('/docs', async (req, res) => {
//FIND MANY
const many = await collection.find( {_id: { $in: ["10006546", "100009090"] } } ).toArray();
//LOGGER
console.log(many)
res.send('docs returned', JSON.stringify(many));
});
//add docs
app.post('/test', async (req, res) => {
//get data through body
const service = req.body.name;
//get data through query params in url
const qparam = req.query.name;
const _id = req.query.id;
//build your doc
let doc = {service:qparam, _id: _id};
//connect to db and collection
//2 set db and collection
//1 connect
const client = await MongoClient.connect(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const collection = client.db("sample_airbnb").collection("listingsAndReviews");
collection.insertOne(doc).then(doc => { console.log('inserted id is: ', doc.insertedId)});
// /res.status(status).send(body)
res.send(JSON.stringify(service + ' ' + qparam));
});
app.listen(port, () => {
});
run();

Why do i get 'undefined' at my request in Node and Mongo?

I'm creating my first API, I've used .env for database variables in this url:
const uri = `mongodb+srv://${mongo_user}:${mongo_pwd}#cluster0.ynvbj.mongodb.net/${mongo_db}?retryWrites=true&w=majority`
then created the client:
const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true })
Then, a main function that calls the functions that finds data from Mongo like users:
async function main() {
try {
await client.connect();
await getUser(client, 'users', mongo_db);
} catch (e) {
console.error(e);
} finally {
await client.close();
}
}
main().catch(console.err);
//------GET
//user by Dni
async function getUser(client, coll, mongo_db){
const collection = await client.db(`${mongo_db}`).collection(`${coll}`)
.find({dni: 38383838}).toArray(function(err, results) {
console.log(results);
return results;
})
};
And finally, I created the routes, but I get undefined. I've check everything and I don't get what's wrong.
const {getUser, getOrder, getProducts} = require('./db_connection')
const {Router} = require('express')
const router = Router()
//GET
//get user
router.get('/users', async (req, res) => {
let data = await getUser;
res.json({data});
})
module.exports = router;
And this is app.js
const express = require('express')
const app = express()
require('dotenv').config();
//settings
app.set('port', process.env.PORT || 3000)
//middlewares
app.use(express.urlencoded({extended:false}))
app.use(express.json())
//server
app.listen(app.get('port'), ()=> {
console.log(`Server running on port ${app.get('port')}`)
})
//require api
app.use('/api', require('./api_routes'))
It looks like your results are undefined, that could be for a lot of different reasons.
Where you have console.log(results) can you replace that with console.debug(err, results);
That might give you more insight into the reason it's not getting the results.
async function getUser(client, coll, mongo_db){
const collection = await client.db(`${mongo_db}`).collection(`${coll}`)
.find({dni: 38383838})
.toArray(function(err, results) {
console.debug(err, results);
return results;
})
};

How to use createConnection of mongoose

I'm using mongoose as below.
// .env
MONGO_URI=mongodb://127.0.0.1:27017/users
And mongoDB runs on app.js.
// app.js
const connect = () => mongoose.connect(process.env.MONGO_URI, dbOptions, (err) => {
if (err) {
return console.error('db error, connect in 5sec');
}
return app.listen(4000, () => console.log('App bounded'));
});
connect();
And there is an api as below.
// api.js
const user = require('./models/userSchema')
router.get('/', async(req, res) => {
const result = await user.find({name: 'test'})
res.send(result)
}
But now I want to make one server using diverse collenction in it.
So I have to change the env file.
// .env
MONGO_URI=mongodb://127.0.0.1:27017
And there are many collections in Mongodb like 'users', 'testers' etc.
And Api works like..
// api.js
const user = require('./models/userSchema')
const testers = require('./models/testerSchema')
router.get('/', async(req, res) => {
const result = await user.find({name: 'test'})
const result2 = await testers.find({name: 'test2'})
res.send(result, result2)
}
I found 'createConnection' But I don't know where I should put this and how to read each database and collections in my arquitecture. Could you recommend some solutions?
Thank you so much for reading it.

Resources