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.