I successfully set up authentication for my app, which is a node.js (Express) app. I used Passport-local and I also used a Mongoose schema.
The folder structure is like this:
app - app.js - config - auth.js - keys.js - passport.js - models - User.js - node_modules - package-lock.json - package.json - routes - index.js - users.js
Inside the “models” folder is the User.js file, and it contains this schema:
const mongoose=require("mongoose"); const UserSchema = new mongoose.Schema({ username: { type: String, required: true, min: 4, max: 255 }, email: { type: String, required: true, min: 6, max: 255 }, password: { type: String, required: true, min: 6, max: 1024 }, group: { type: Number }, score: { type: Number }, date: { type: Date, default: Date.now } }); const User = mongoose.model("User", UserSchema); module.exports = User;
And the app.js (the main file) includes the following code. I am not showing everything by the way. The connection string is in the keys.js file.
const mongoose=require("mongoose"); const db = require('./config/keys').mongoURI; const app = express(); mongoose .connect( db, { useNewUrlParser: true ,useUnifiedTopology: true} ) .then(() => console.log('MongoDB Connected')) .catch(err => console.log(err));
For operations that don’t require the database, such as opening an .ejs file, using “GET”, there is no problem. I am able to do them.
And I can register and login a user, and that user appears in the Mongodb collection called “users” with the correct fields showing. This collection did not exist before I added users using the registration and login forms in the web app.
Now I want to test CRUD operations using the same collection (“users”) in the database. So I tried this:
app.get("/leaderboard", function(req, res) { db.collection("users") .find({}, { projection: { _id: 0, username: 1, score: 1 } }) .sort({score:-1}) .limit(1) .toArray(function(err, result) { if (err) res.send({ status: false, msg: "failed to retrieve players" }); console.log(Array.from(result)); res.send({ status: true, msg: result }); }) });
Before I added authentication to the api, I was able to get the result of this and all other queries to the database without any problem.
The collection name though was different and I used a different connection script for the database. It was like this below. The MONGO_URI was stored as a string in a dotenv file. I didn’t have a Mongoose schema either. I only had the index.js file; no other folders.
// Database Connection Info const MongoClient = require('mongodb').MongoClient; const uri = process.env.MONGO_URI; let db; // Connect to the database with uri (async () => { let client = await MongoClient.connect( uri, { useNewUrlParser: true, useUnifiedTopology: true } ); db = client.db('name'); app.listen(PORT, async function() { console.log('Listening on Port ${PORT}'); if (db) { console.log('Database is Connected!'); } }); })();
In the past, I used async functions to get the leaderboard result. It was with a different collection (“myCollection”) but with the same database.
app.get("/leaderboard", async function(req, res) { try { await db.collection("myCollection") .find({}, { projection: { _id: 0, username: 1, score: 1 } }) .sort({score:-1}) .limit(1) .toArray(function(err, result) { if (err) res.send({ status: false, msg: "failed to retrieve players" }); console.log(Array.from(result)); res.send({ status: true, msg: result }); }) } catch(err) {console.log("It failed")} });
But even though the above worked well before I added authentication and added the Mongoose model, I can’t get the leaderboard result to show now. I am getting a “MongoDB connected” message on the console after I enter “node app” in the terminal and I get no error messages. This is before I try getting the leaderboard.
The error message I get on the console after I try and get the leaderboard is:
TypeError: db.collection is not a function
I have also tried the below script without success:
app.get("/leaderboard", function(req, res) { User .find({}, { projection: { _id: 0, username: 1, score: 1 } }) .sort({score:-1}) .limit(1) .toArray(function(err, result) { if (err) res.send({ status: false, msg: "failed to retrieve players" }); console.log(Array.from(result)); res.send({ status: true, msg: result }); }) });
I want to use async functions for the database queries but I can’t find a script for connecting that works with the new way I am connecting: “mongoose.connect”.
I feel I have to use the “mongoose.connect” way of connecting instead of the “const uri = process.env.MONGO_URI;” way because I am using mongoose schemas for the authentication. And this schema automatically creates the collection of users who register. (I don’t have any real users; I am using fake user profiles to test the process.) I am also assuming there is a difference between mongoose and mongodb.
It would be best if I could use the old way as I could use async/await with the scripts. However, the main problem is I am not getting any result at all and only error messages although I was able to do many operations involving the database before I added authentication.
Advertisement
Answer
I was able to solve this problem by writing the connection to the mongodb like this:
const MONGO_URI="mongodb+srv://<username>:<password>@<insert string><database>?retryWrites=true&w=majority"; mongoose .connect(MONGO_URI, {useNewUrlParser: true, useUnifiedTopology: true}) .then(console.log(`MongoDB connected ${MONGO_URI}`)) .catch(err=>console.log(err));
Then I wrote the query like this:
app.get("/leaderboard", async function(req, res) { const client = await mongoose.connect(MONGO_URI, function (err, db){ if (err) throw new Error(err); db.collection("users").find({}, { projection: { _id: 0, username: 1, score: 1 } }) .sort({score:-1}).limit(2) .toArray(function(err, result) { if (err) throw new Error(err); console.log(Array.from(result)); res.send({ status: true, msg: result }); db.close(); //I think I can omit this line. }); }); });
Before I made the function async though, I got an error report:
Error: MongoError: Cannot use a session that has ended
I have to make these scripts async.
I left the mongoose schema as is. I was still able to authenticate users, and they were added to the database.
This link SO Documentation: Node.js Mongodb integration helped me. SO is a documentation site made by StackOverflow contributors.