6

I am getting error while trying to save session on mongodb. Here is my code..

const express = require("express");
const session = require("express-session");
const MongoStore = require("connect-mongo").default;
const app = express();

let sessionOptions = session({
  secret: "JavaScript is cool",
  store: MongoStore.create({ client: require("./db") }),
  resave: false,
  saveUninitialized: false,
  cookie: { maxAge: 1000 * 60 * 60 * 24, httpOnly: true },
});

app.use(sessionOptions);
const router = require("./router");
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(express.static("public"));
app.set("views", "views");
app.set("view engine", "ejs");

app.use("/", router);

module.exports = app;

and db.js

const dotenv = require("dotenv");
dotenv.config();
const mongodb = require("mongodb");

mongodb.connect(
  process.env.CONNECTIONSTRING,
  { useNewUrlParser: true, useUnifiedTopology: true },
  (err, client) => {
    module.exports = client;
    const app = require("./app");
    app.listen(process.env.PORT);
  }
);

And the error is here..

Assertion failed: You must provide either mongoUr|clientPromise in options
/home/irfan/Desktop/Brad_Sciff/Complex_App/node_modules/connect-mongo/build/main/lib/MongoStore.js:121
            throw new Error('Cannot init client');
            ^

    Error: Cannot init client
        at new MongoStore (/home/irfan/Desktop/Brad_Sciff/Complex_App/node_modules/connect-mongo/build/main/lib/MongoStore.js:121:19)
        at Function.create (/home/irfan/Desktop/Brad_Sciff/Complex_App/node_modules/connect-mongo/build/main/lib/MongoStore.js:137:16)
        at Object.<anonymous> (/home/irfan/Desktop/Brad_Sciff/Complex_App/app.js:9:21)
        at Module._compile (internal/modules/cjs/loader.js:778:30)
        at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
        at Module.load (internal/modules/cjs/loader.js:653:32)
        at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
        at Function.Module._load (internal/modules/cjs/loader.js:585:3)
        at Module.require (internal/modules/cjs/loader.js:692:17)
        at require (internal/modules/cjs/helpers.js:25:18)

I tried to change from const MongoStore = require("connect-mongo").default to const MongoStore = require("connect-mongo")(session)

But the error is showing..

const MongoStore = require("connect-mongo")(session);
                                           ^

TypeError: require(...) is not a function
    at Object.<anonymous> (/home/irfan/Desktop/Brad_Sciff/Complex_App/app.js:4:44)
    at Module._compile (internal/modules/cjs/loader.js:778:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Function.Module._load (internal/modules/cjs/loader.js:585:3)
    at Module.require (internal/modules/cjs/loader.js:692:17)
    at require (internal/modules/cjs/helpers.js:25:18)
    at mongodb.connect (/home/irfan/Desktop/Brad_Sciff/Complex_App/db.js:10:17)
    at /home/irfan/Desktop/Brad_Sciff/Complex_App/node_modules/mongodb/lib/utils.js:693:5

Using Connect-mongo 4.2, express-session 1.17.1 express 4.17.1 mongodb 3.6.4

Don't know what I am missing.

Please help.

Thanks in Advance.

Irfan.

Irfan Habib
  • 148
  • 1
  • 1
  • 8

8 Answers8

10

So it looks like connect-mongo has been updated recently. I came across this issue today as well and here's how I fixed it.

How it used to be:

const session = require('express-session');
const MongoStore = require('connect-mongo')(session);

app.use(
  session({
    ...options
    store: new MongoStore({ mongooseConnection: mongoose.connection  }),
  })
);

How it is now:

const session = require('express-session');
const MongoStore = require('connect-mongo').default;

app.use(
  session({
    store: MongoStore.create({ mongoUrl: process.env.MONGO_URI }),
    ...options
  })
);

Try passing your connection string into mongoURL instead of client and see if that helps.

You can read more about connect-mongo in their docs.

Romaan Naeem
  • 116
  • 1
  • 6
  • In the solution you provide the app creates an extra connection for the session handler instead of using the existing connection. – elMeroMero Apr 27 '21 at 06:35
  • @elMeroMero I don't see what you're referring to, where is there an extra connection? – Romaan Naeem Apr 28 '21 at 20:29
  • I assume that Irfan uses mongoose to persist data in his project. The old approach of ``` store: new MongoStore({ mongooseConnection: mongoose.connection }),``` allows him to use the already existing connection use by the app in other places for the session store as well. Now by replacing it with ```store: MongoStore.create({ mongoUrl: process.env.MONGO_URI }),``` you are basically asking connect-mongo to create a new connection from scratch - resulting in two connects.. – elMeroMero Apr 28 '21 at 23:37
  • This is not the correct solution, as it promotes multiple connection to mongodb 1. By mongodb.connect 2. By MongoStore.create @elMeroMero also suggested the same – Ritik May 21 '21 at 13:38
  • There is a solution now in the updated [Migration guide of mongo-connect](https://github.com/jdesboeufs/connect-mongo/blob/master/MIGRATION_V4.md): "To reuse an existing mongoose connection retreive the mongoDb driver from you mongoose connection using Connection.prototype.getClient() and pass it to the store in the client-option." – elMeroMero May 21 '21 at 16:51
3

the following is worked for me.

const mongoose = require("mongoose");
const session = require("express-session");
const MongoStore = require("connect-mongo");

const url = 'mongodb://localhost/mydb';
mongoose.connect(url, { useNewUrlParser: true, useCreateIndex: true, 
useUnifiedTopology: true, useFindAndModify: true });
const connection = mongoose.connection;

connection.once('open', () => {
    console.log("database connected successfully...");
}).catch(err => {
    console.log("connection failed...");
});

// session store

let store = new MongoStore({
   mongoUrl: url,
   collection: "sessions"
});

 // session config

 app.use(session({
   secret: process.env.COOKIE_SECRET,
   resave: false,
   store: store,
   saveUninitialized: false,
   cookie: { maxAge: 1000 * 60 * 60 * 24 }, // 24 hours
 }));
1

what I did was to downgrade from mongodb version 4 to version 3. Hence, in your terminal, uninstall connect-mongo and reinstall lower version.

"npm uninstall connect-mongo"

"npm i connect-mongo@3"

this allows you to be able to pass in your session to the MongoStore.

"const path = require('path')

const express = require('express') 

const mongoose  = require ('mongoose')

const dotenv = require('dotenv')

const morgan = require('morgan')

const exphbs = require ('express-handlebars')

const passport = require('passport')

const session = require ('express-session')

const MongoStore = require('connect-mongo')(session)"
Aqib Javed
  • 935
  • 1
  • 5
  • 15
Adeniyi
  • 21
  • 1
1

I'm a bit late to this party, but I was struggling with this exact problem and I managed to figure out a way to do it reusing the existing database connection instead of creating a new one by putting our db URI in the mongoUrl property as others have answered, so here is my solution for any future readers:

Basically, if we want to re-use our database connection from elsewhere in our project, MongoStore needs a 'client', or 'clientPromise'.

Now, the Mongo client is not available until we have connected to the database (as far as I understand, the client is just a reference to the open connection) - and when we first start our server, it will take a little while to connect.

Therefore, we need to manually create a Promise to give to MongoStore's 'clientPromise' property, that will resolve to the client when the connection is finally established.

For this, we can add an event listener to the mongoose.connection so that when the connection is established, we then call getClient() on the mongoose.connection to obtain the client, and then resolve our Promise with it.

const session = require("express-session");
const mongoose = require("mongoose");
const MongoStore = require("connect-mongo");

const mongoClientPromise = new Promise((resolve) => {
    mongoose.connection.on("connected", () => {
        const client = mongoose.connection.getClient();
        resolve(client);
    });
});

const sessionStore = MongoStore.create({
    clientPromise: mongoClientPromise,
    dbName: "myDb",
    collection: "sessions"
});

app.use(session({
    secret: mySecret,
    store: sessionStore
});
jkbb93
  • 11
  • 1
0

This is latest solution

add this lines

First

var session = require('express-session');
var MongoStore = require ('connect-mongo');

Second

app.use(session(
      {
        secret: '**something** like mysupersecret',
        store: MongoStore.create({ 
          mongoUrl: '**your url**like mongodb://localhost/test-app'}),
        
      }));
sainupangad
  • 115
  • 9
0

There is an another npm package connect-mongodb-session, https://www.npmjs.com/package/connect-mongodb-session Install it and it should work.

const MongoDBStore = require('connect-mongodb-session')(session);

Piyush Chandra
  • 159
  • 1
  • 4
0

This worked for me,

const session = require('express-session');
const MongoStore = require('connect-mongo');

const dbUrl = process.env.DB_URL;

app.use(session({
    secret: 'thisismysecret',
    resave: false,
    saveUninitialized: false,
    store: MongoStore.create( {
        mongoUrl: dbUrl,
        touchAfter: 24 * 3600 
     }) }));
0

So I also faced the same problem. And fixed by

  1. not using (session) and .default with MongoStore = require('connect-mongo')

  2. Removing new with MongoStore

  3. Using either of client: mongoose.connection.getClient(), // if you want to reuse the mongo client connection or mongoUrl:"mongodb://localhost:27017/DBNAME",

    var mongoose        =   require('mongoose');
    var session         =   require('express-session');
    var MongoStore = require('connect-mongo') // no .default or no (session)
    
    // process.env.databaseURL if you are using .env file 
    // or else you can straight away pass the db link (not recommended this way though)
    mongoose.connect(process.env.databaseURL,{useNewUrlParser: true, useUnifiedTopology: true}).catch(error => console.log("App.js mongoose.connect error",error));
    
    var db = mongoose.connection;
    db.on('error', console.error);
    db.once('open', function(){
        console.log("App is connected to DB", db.name)
    });
    mongoose.Promise = global.Promise;
    
    app.use(session({
        secret: 'Your secret here',
        saveUninitialized: false,
        resave: false,
        store: MongoStore.create({
          client: mongoose.connection.getClient(), //  as per the new versioning use client here and you can pass either the link or the same mongoose connection
          //Use above client if you want to use an existing connection
    
    
          // mongoUrl:"mongodb://localhost:27017/DBNAME",
          // or use this mongoUrl if you are passing the database URL straight away
    
          ttl: 1 * 6 * 60 * 60,  //ttl: 14 * 24 * 60 * 60, //days * hours * minutes * seconds
          autoRemove: 'native' // Default
        })
    }));
    
Sukhpreet Singh
  • 501
  • 4
  • 4