简体   繁体   中英

Mongoose Connection with MongoDB

I've been working with Mongoose/MongoDB and I realized I don't quite understand how the connection between mongoose and my MongoDB database persists. Let's say I have Node application with a server.js file.

const express = require('express')
const dotenv = require('dotenv')
dotenv.config()

const connectDB = require('./config/db')

connectDB()

const app = express()

app.get('/', (req, res) => {
  res.send('API is running...')
})

const PORT = process.env.PORT || 5000

app.listen(
  PORT,
  console.log(`Server running in ${process.env.NODE_ENV} port ${PORT}`)
)

And then I have my database configuration file where the connection is initiated.

const mongoose = require('mongoose')

const connectDB = async () => {
  try {
    const conn = await mongoose.connect(process.env.MONGO_URI, {
      useUnifiedTopology: true,
      useNewUrlParser: true,
      useCreateIndex: true,
    })

    console.log(`MongoDB Connected: ${conn.connection.host}`)
  } catch (error) {
    console.error(`Error: ${error.message}`)
    process.exit(1)
  }
}

module.exports = connectDB

My question is, after the database is connected with mongoose.connect(), how does it persist throughout the rest of the application? Obviously, this allows me to interact with the database in other files, but I'm not quite clear on what's going on "underneath the hood". Let's say I have a Product model

const mongoose = require('mongoose')

const reviewSchema = mongoose.Schema(
  {
    name: { type: String, required: true },
    rating: { type: Number, required: true },
    comment: { type: String, required: true },
  },
  {
    timestamps: true,
  }
)

const productSchema = mongoose.Schema(
  {
    user: { type: mongoose.Schema.Types.ObjectId, required: true, ref: 'User' },
    name: { type: String, required: true },
    image: { type: String, required: true },
    brand: { type: String, required: true },
    category: { type: String, required: true },
    description: { type: String, required: true },
    reviews: [reviewSchema],
    rating: { type: Number, required: true, default: 0 },
    numReviews: { type: Number, required: true, default: 0 },
    price: { type: Number, required: true, default: 0 },
    countInStock: { type: Number, required: true, default: 0 },
  },
  {
    timestamps: true,
  }
)

const Product = mongoose.model('Product', productSchema)

module.exports = Product

I can then have a route

router.get(
  '/',
  asyncHandler(async (req, res) => {
    const products = await Product.find({})
    res.json(products)
  })
)

Which would operate on this Product model. My question is, how is mongoose aware of the database still (after the initial connection) that allows it to call methods on this Model that allow it to interact with the database.

I'm not an expert in MongoDB nor JavaScript but I think that you might have misunderstood some concepts.

Mongoose as it says on its website provides a schema-based solution module to your application data.

You use mongoose to connect with the database through its methods, the database is not connected with mongoose.

Dotenv file basically stores your server configuration data within your project separating it from the code that allows you to display port info, processes etc in a clear way.

Let's start reviewing the first file

Here you create some const of express and dotenv

const express = require('express')
const dotenv = require('dotenv')

After that, you apply the config method to the dotenv const (you can also add that method at the first line)

dotenv.config()

Now you create the connectDB const loading the content from the DB file (which I suppose it's the next one)

const connectDB = require('./config/db')

Calling the method of the class starts the connection with the Database:

connectDB()

Now you use express to make a get request that will be displayed on your root folder and basically will display some basic data from the port and the process (loaded from ENV file)

const app = express()

app.get('/', (req, res) => {
  res.send('API is running...')
})

const PORT = process.env.PORT || 5000

app.listen(
  PORT,
  console.log(`Server running in ${process.env.NODE_ENV} port ${PORT}`)
)

Let's review now the other file.

You create a const from mongoose

const mongoose = require('mongoose')

And establish a connection to your DB

const connectDB = async () => {
  try {
    const conn = await mongoose.connect(process.env.MONGO_URI, {
      useUnifiedTopology: true,
      useNewUrlParser: true,
      useCreateIndex: true,
    })

    console.log(`MongoDB Connected: ${conn.connection.host}`)
  } catch (error) {
    console.error(`Error: ${error.message}`)
    process.exit(1)
  }
}

This is the important part, you export that const to the whole project so it can be accessed from different parts of the project that's the reason that allows you to connect with the DB without providing the same credentials every single time, and the same logic applies to dotenv file

module.exports = connectDB 

((If you're not familiar with Java just ignore this:))

I was working today with MySQL and Java JDBC API and I created some files that allow me to interact with the DB like you're doing with Mongoose, see:

//variables to get DB info and connection
private Connection connection;
    private PreparedStatement stmt = null;
    private ResultSet rs = null;
    private Statement statement = null;
//Constructor, when you create an object of this class will establish a connection 
with the dbCredentials variables (like dotenv)
  public DevOps() throws SQLException {

        this.connection = DriverManager.getConnection(dbCredentials.getDbname(), dbCredentials.getUsername(), dbCredentials.getPass());

    } 

This would be like your dotenv file, you just have to change the data here instead of changing lots of lines of code when you migrate your DB or update credentials

public class dbCredentials {

    public final static String username = "user";
    public final static String pass = "password";
    public static String dbname = "jdbc:mysql://192.168.56.1/school";

    public static String allowMultiQueries() {
        return dbname+"?allowMultiQueries=true";
    }

    public static String getUsername() {
        return username;
    }

    public static String getPass() {
        return pass;
    }

    public static String getDbname() {
        return dbname;
    }
}

And you basically just have to do this:

DevOps devops = new DevOps();

which would be similar to:

const connectDB = require('./config/db')

So basically the concept of all of this is to keep your code organized, ofc you can have all of that on a single file but in terms of scalability would be painful to maintain especially when you have to apply any change to your DB.

Hope this was useful for you, just ask me if you have any doubt!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM