Skip to content
Advertisement

How do I query the database using Mongodb after I set up a Mongoose schema for authentication?

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.

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement