Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why would an object's prototype be forgotten?

Introduction

So I am building a website with node.js, express, express-session, and sequelize.js. Once a user logs in, an instance of the Sequelize model User is created. In my route for user log-in (/auth/login), I have:

var user = (await User.findAll(
        {where: {
            username: username
        }}))[0];

and I few lines down I assign that user to the session.

req.session.user = user;

And then I can persist any changes by simply calling the save method of req.session.user:

await req.session.user.save();

And indeed, if I add this line next:

console.log(Object.getPrototypeOf(req.session.user));

the output is [object SequelizeInstance:User]. So far so good.

Here is the problem

In another route (/users/myaccount/edit-bio) I am able to access the values of req.session.user. That is, the output of

console.log(req.session.user.username);

is seanletendre, as expected. But now when I call

await req.session.user.save();

all I get is the error message:

UnhandledPromiseRejectionWarning: TypeError: req.session.user.save is not a function

"That is weird," I thought, "isn't this the same object?" To investigate, I add the line:

console.log(Object.getPrototypeOf(req.session.user));

just as I did in the log-in route. And what is the output? It is: [object Object]. So it seems that somehow the prototype of req.session.user gets forgotten. I don't understand how this can be.

Is it possible to re-assign a prototype to a plain object?

Suspect A

Based on the comments to my question, I suspect that the prototype is lost when the session manager serializes req.session. It seems that, unlike I thought before,req.session does not point to the exact same session object for different requests. Each time a request ends, it serializes and stores req.session. Then upon receiving a new request with a cookie designating it as part of the same session, the session object is fetch from the session store.

This is how my session middleware is setup:

var session = require('express-session');
//
// yada, yada, yada
//
app.use(session({
    secret: process.env.SESSION_SECRET,
    resave: false,
    saveUninitialized: false,
    cookie: {secure: true}
}));

So what surprises me is that, even though I am using the default store MemoryStore, my sessions are still serialized.

My question now becomes: how can I prevent object serialization upon session store when using MemoryStore?

like image 368
Sean Letendre Avatar asked Nov 16 '20 07:11

Sean Letendre


People also ask

How to check object prototype in JavaScript?

The Object. getPrototypeOf() method returns the prototype (i.e. the value of the internal [[Prototype]] property) of the specified object.

Does object have prototype?

Every object in JavaScript has a built-in property, which is called its prototype. The prototype is itself an object, so the prototype will have its own prototype, making what's called a prototype chain.

How do I access prototype objects?

We can access the function's prototype property using functionName. prototype . As seen from the above image prototype property of the function is an object (prototype object) with two properties: constructor property which points to Human function itself.


Video Answer


1 Answers

In express-session the method save() is exposed by the object session into the request object (docs), eg.:

req.session.save(callback)

Your code req.session.user.save() is wrong, the correct way is req.session.save(), diff.:

req.session.user.save();
-----------^^^^^
req.session.save()

The method save() isn't a Promise, you must pass a callback for wait for the result of the save:

req.session.user = user;
req.session.save(function(err) {
  if( err ){
    // session not saved
  } else {
    // session saved
  }
})

you can transform it into a Promise (and await it), in this way:

const saveSession = (req) => {
  return new Promise((resolve, reject) => {
    req.session.save(function(err) {
      if( err ){
        reject(err);
      } else {
        resolve(true);
      }
    });
  });
};

req.session.user = user;
await saveSession(req);

The method save() is automatically called at the end of the HTTP response if the session data has been altered. Because of this, typically this method does not need to be called.

UPDATE

I call req.session.user.save() because I want to save the Sequelize Model instance into my database.

express-session use JSON.stringify() for serialize the session into a string and store into a session storage (more info). JSON.stringify() doesn't understand functions and only property are stored. For this reason, your save() function is lost.

like image 197
Simone Nigro Avatar answered Oct 17 '22 14:10

Simone Nigro