trying to get my code to connect to mongodb.
I have start mongo db compass, rund mongosh and mongod in cmd
add a new db to mongodb.
edit a new url to connect.
run npm start and get this and i dont connect to mongodb:
(node:14420) [MONGODB DRIVER] Warning: Current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version.
To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor.
(Use `node --trace-warnings ...` to show where the warning was created)
my db.ts
import { MongoClient } from 'mongodb';
// Connection URL
const url = 'mongodb://127.0.0.1:27017';
// Database Name
const dbName = 'xxxxx';
// Create a new MongoClient
export async function initMongo() {
try {
const client = new MongoClient(url,{ useNewUrlParser: true });
await client.connect();
const db = client.db(dbName);
at the bottom of db
} catch (e) {
console.log('Cannot connect to MongoDB. Will retry in 10 seconds ...');
await new Promise<void>(resolve => {
setTimeout(() => resolve(), 10000);
});
console.log('Retrying...');
return await initMongo();
}
await client.connect
connect have a line right true.
Related
I am working on a Next.js project and I want to connect it with MongoDB using mongoose. I want to make a middleware to run the connect function inside so I don't have to call connect function in each file I am using a mongoose model in.
this is my connect file:
// src/utils/connect.js
import mongoose from 'mongoose'
// getting the connection uri
const MONGO_URI = process.env.MONGO_URI
// checking if MONGO_URI is defined
if (!MONGO_URI) {
throw new Error(
'Please define the MONGO_URI environment variable inside .env.local'
)
}
// maintaining a cached connection to prevent reconnection (connections growing exponentially during API Route usage)
let cached = global.mongoose
// restiing the connection if there was no cached connection
if (!cached) {
cached = global.mongoose = null
}
/**
* mongodb connection
*/
export default async function connect() {
// returning the cached connection if it exists
if (cached) {
return cached
}
cached = await mongoose.connect(MONGO_URI, {
bufferCommands: false,
useNewUrlParser: true,
useUnifiedTopology: true
})
return cached
}
it will connect mongoose with my MongoDB database and caches the connection for future use.
this is one of my example API routes:
// src/pages/api/test.js
import connect from '../../utils/connect'
import Test from '../../models/Test';
export default async function handler(req, res) {
// connect
await connect()
const data = await Test.find({});
res.status(200).json({ name: 'John Doe', data })
}
now what I want to do is to get rid of that await connect() function in all routes and instead make a middleware to run the connect. and also is it better do so or not?
I was creating a website using NextJS and for authentication uses NextAuth. And the database is on free tier in MongoDB Atlas.
I have two versions of code for database connection. One is this:
/**
* MongoDB Connection
*
* */
import mongoose from 'mongoose'
const MONGODB_URI = process.env.MONGODB_URL
if (! process.env.MONGODB_URL) {
throw new Error(
'Please define the MONGODB_URI environment variable inside .env.local'
)
}
/**
* Global is used here to maintain a cached connection across hot reloads
* in development. This prevents connections growing exponentially
* during API Route usage.
*/
let cached = global.mongoose
if (!cached) {
cached = global.mongoose = { conn: null, promise: null }
}
async function dbConnect() {
if (cached.conn) {
return cached.conn
}
if (!cached.promise) {
const opts = {
useNewUrlParser: true,
useUnifiedTopology: true,
bufferCommands: false,
// bufferMaxEntries: 0,
// useFindAndModify: false,
// useCreateIndex: true,
}
cached.promise = mongoose.connect(process.env.MONGODB_URL, opts).then((mongoose) => {
return mongoose
})
}
cached.conn = await cached.promise
return cached.conn
}
export default dbConnect
So, before making any DB related queries via code, I call await dbConnect(). It is working fine.
But for storing the sessions in DB, in NextAuth, I was not able to use the above function. So for that, am using this custom code (/lib/mongodb.js):
/**
*
* Used only for Next-Auth
*
*/
import { MongoClient } from "mongodb"
const uri = process.env.MONGODB_URL
const options = {
useUnifiedTopology: true,
useNewUrlParser: true,
}
let client
let clientPromise
if (!process.env.MONGODB_URL) {
throw new Error("Please add your Mongo URI to .env.local")
}
if (process.env.NODE_ENV === "development") {
// In development mode, use a global variable so that the value
// is preserved across module reloads caused by HMR (Hot Module Replacement).
if (!global._mongoClientPromise) {
client = new MongoClient(uri, options)
global._mongoClientPromise = client.connect()
}
clientPromise = global._mongoClientPromise
} else {
// In production mode, it's best to not use a global variable.
client = new MongoClient(uri, options)
clientPromise = client.connect()
}
// Export a module-scoped MongoClient promise. By doing this in a
// separate module, the client can be shared across functions.
export default clientPromise
And the code in my /pages/api/auth/[...nextauth].js is like this:
import NextAuth from 'next-auth'
import { MongoDBAdapter } from "#next-auth/mongodb-adapter"
import mongodb from '../../../lib/mongodb'
//...
export default async function auth(req, res) {
return await NextAuth(req, res, {
//....
adapter: MongoDBAdapter({
db: (await mongodb).db("my_db_name_here")
}),
//....
})
}
Here's the packages am using:
"mongodb": "^4.1.2",
"mongoose": "^6.0.1",
"next": "11.0.1",
"next-auth": "^4.0.0-beta.6",
"react": "17.0.2",
"react-dom": "17.0.2",
The problem is, am receiving email notifications sometimes like the following:
My website is still in testing phase(tested by two persons only) and is hosting in Vercel server. I believe this could be because NextAuth is creating new db connections each time? Any thoughts on what went wrong?
clientPromise in next-auth is local, you create new client and 5 connections every time. Just use global.mongoose.conn.
The docs for MongoDBAdapter says it needs a promise that resolves to a client, so it must be something like this:
export default NextAuth({
adapter: MongoDBAdapter(dbConnect().then(mon => mon.connection.getClient())),
...
})
In you case you seem to use db. I couldn't find any references for MongoDBAdapter to accept something liek {db: ...} but you can get the db from mongoose as following:
await (dbConnect().then(mon => mon.connection.getClient().db("my_db_name_here")))
Or without parameter to use the same database as configured in mongoose connection.
UPDATE
The issue with number of connections from Vercel is covered in Vercel creates new DB connection for every request
As you know NextJS is Jamstack framework and I'm migrating from node/express to it but my problem is how to connect server to database at startup of server as i did in express?
there is now where to put my initalizing code in NextJS? Am I saying correct?
I saw some code to to that but there were typescript codes that im not familiar with them
On the other hand i'm able to do that on serverside functions like getStaticProps or getServerSideProps this is my code
dbinit.js
import mongoose from "mongoose";
export const dbStatus = () => mongoose.connection.readyState;
export default function dbConnect() {
if (dbStatus == 1) return "database is connected";
mongoose.connect(
`mongodb://localhost:${process.env.DBPORT}/${process.env.DBNAME}`
);
}
index.js
export async function getServerSideProps() {
const result = await dbConnect();
console.log(dbStatus());
return {
props: {},
};
}
with this code im able to connect to mongodb but there are some problems and the most important is that mycode isnot cleancode
In NextJS, we can connect MongoDB as middleware. This is very similar to the Express middleware approach as shown below.
// middleware/database.js
import { MongoClient } from 'mongodb';
import nextConnect from 'next-connect';
const client = new MongoClient('{YOUR-MONGODB-CONNECTION-STRING}', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
async function database(req, res, next) {
if (!client.isConnected()) await client.connect();
req.dbClient = client;
req.db = client.db('MCT');
return next();
}
const middleware = nextConnect();
middleware.use(database);
export default middleware;
For more details, you can refer to this official how-to doc for step-by-step guidance. Here is the example repository used.
Here I am using the official mongodb driver btw.
const { MongoClient, ServerApiVersion } = require('mongodb');
const CLient = new MongoClient(process.env.uri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1});
const db = CLient.db("Discord").collection("Discord");
CLient.connect(err => {
//const db = CLient.db("Discord").collection("Discord");
console.log("Connected to the Database")
//CLient.close();
const prefix = await db.find({guildid: msg.guild.id});
await db.updateOne({guildid: msg.guild.id}, {$set: {prefix: newPrefix}});
if (!prefix) {
let prefix = db.insertOne({guildid: msg.guild.id, prefix: newPrefix})
return msg.channel.send(`Your server prefix is now ${newPrefix}`);
}
return msg.channel.send(`Your server prefix is now ${newPrefix}`);
});
msg is already defined here as a object btw and newPrefix is also defined. So the database connected successfully according to the logs. The error is thrown when the code is run
MongoServerError: BSON field 'update.apiVersion' is an unknown field this is the error msg. How can i fix this
I had the same problem. This is related to how you instantiate the client - i.e., this won't fail:
const Client = new MongoClient(process.env.uri);
This is a very weird problem with "connect-mongo"
In my server, I have two scripts.
1) create the express server with session with Mongo DataStore: It has no problem for connection or creating the session.
MongoStore = require('connect-mongo'),
app = require('express').createServer(
express.session({ secret: cfg.wiki_session_secret,
store:new MongoStore({
db: 'mydatabase',
host: '10.10.10.10',
port: 27017
})
})
);
2) just create the store without express:
var MongoStore = require('connect-mongo');
var options = {db: 'mydatabase'};
var store = new MongoStore(options, function() {
var db = new mongo.Db(options.db, new mongo.Server('10.10.10.10', 27017, {}));
db.open(function(err) {
db.collection('sessions', function(err, collection) {
callback(store, db, collection);
});
});
});
That will throw the connection problem:
node.js:134
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: Error connecting to database
at /home/eauser/node_modules/connect-mongo/lib/connect-mongo.js:106:13
at /home/eauser/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/db.js:79:30
at [object Object].<anonymous> (/home/eauser/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connections/server.js:113:12)
at [object Object].emit (events.js:64:17)
at Array.<anonymous> (/home/eauser/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection.js:166:14)
at EventEmitter._tickCallback (node.js:126:26)
I just don't know why..
connect-mongo is a middleware for the connect framework, which express is based on.
So, you must use the middleware with the express framework or the connect framework, otherwise it won't work. It's not written to be a standalone session library.
You can go for mongoose to connect. Install using npm command
npm install mongoose
Install mongoose globally
npm install -g mongoose
app.js
var mongoose = require("mongoose");
This module has callback in the constructor which is called when the database is connected, and the collection is initialized so it won't work as you expect.
I've the same problem than you and I wanted the same interface that you aim here. So I wrote another module called YAMS - Yet Another Mongo Store. This is an example with YAMS:
var MongoClient = require("mongodb").MongoClient;
var Yams = require('yams');
var store = new Yams(function (done) {
//this will be called once, you must return the collection sessions.
MongoClient.connect('mongo://localhost/myapp', function (err, db) {
if (err) return done(err);
var sessionsCollection = db.collection('sessions')
//use TTL in mongodb, the document will be automatically expired when the session ends.
sessionsCollection.ensureIndex({expires:1}, {expireAfterSeconds: 0}, function(){});
done(null, sessionsCollection);
});
});
app.usage(express.session({
secret: 'black whisky boycott tango 2013',
store: store
}));
This is in my opinion more flexible than the connect-mongo middleware.