2

I am new to nodejs/express and currently trying to figure out how to store objects of a certain class in session storage (which I understand is basically a serialized version of the req.session object).

My (shortened example is this):

//cart.js
const CartItem = require('./cartItem');

class Cart {

    _items = [];

    constructor() {
        // ...
    }

    // ...

    addItem(item) {
        // ...
    }
}

module.exports = Cart;
//routes.js    
router.get('/create', function (req, res, next) {
    req.session.cart = new Cart();
    req.session.cart.addItem(new CartItem('Red Sox', 15, Math.round(Math.random() * 5)));

    console.log(req.session);

    res.render('cart/create.twig', {
        cart: req.session.cart
    });
});

router.get('/overview', function (req, res, next) {
    console.log(req.session);

    res.render('cart/overview.twig', {
        cart: req.session.cart
    });
});

The problem I am facing is that during the first action (router.get('/create') the req.session.cart property holds an object of the Cart class.

However, when loading from the session in the second action (router.get('/overview') the req.session.cart property holds an object of no special class.

I assume/understand that JavaScript cannot know during deserialization what kind of object this JSON once has been. But how do other people solve this issue ?

I have been checking out other session middlewares (I currently use express-session), but none of them mention in detail, whether they are able to do what I am looking for.

Please help me understand and resolve this issue :)

Thanks everyone!

shaochuancs
  • 15,342
  • 3
  • 54
  • 62
Jan
  • 907
  • 1
  • 8
  • 23
  • 2
    I would usually add a new method called `toJSON()` that would return a JSON-friendly object/string you can then use later on. – Coder100 Feb 16 '21 at 23:10
  • 1
    …and then also explicitly [create another instance](https://stackoverflow.com/a/11810861/1048572) using a `Cart.fromJSON()` method – Bergi Feb 17 '21 at 03:42
  • I think @Bergi 's comment kind of is the right answer here. It's the most "vanilla" way of doing things in plain Node/Javascript without additional dependencies - so that's what I will be trying. Thanks ! – Jan Feb 23 '21 at 21:26

1 Answers1

1

I assume/understand that JavaScript cannot know during deserialization what kind of object this JSON once has been.

You are right, the property value in req.session would be serialized as JSON by store (check express-session document), and the default serialization/deserialization of JavaScript (JSON.stringify and JSON.parse) does not recognize Class/Type information, that is the root cause of this problem. It is a general issue on JavaScript serialization.

To solve this general issue and retain Class/Type information during JavaScript serialization/deserialization, I've made an npm module named esserializer. It is pretty easy to sovle this Cart/CartItem problem with esserializer:

For GET /create interface, you can serialize req.session.cart with esserializer:

const ESSerializer = require('esserializer');
const Cart = require('./Cart');
const CartItem = require('./CartItem');

router.get('/create', function (req, res, next) {
    req.session.cart = new Cart();
    req.session.cart.addItem(new CartItem('Red Sox', 15, Math.round(Math.random() * 5)));

    res.render('cart/create.twig', {
        cart: ESSerializer.serialize(req.session.cart)
    });
});

Once we want to reconstruct this Cart object, with CartItem inside, just invoke the deserialize method of esserializer, and pass all related Classes as parameter:

const ESSerializer = require('esserializer');
const Cart = require('./Cart');
const CartItem = require('./CartItem');

router.get('/overview', function (req, res, next) {
    const cartInstance = ESSerializer.deserialize(serializedString, [Cart, CartItem]);
    console.log(cartInstance);
    //...
});

This reconstructed cartInstance object has all Class/Type information retained, including instance methods, if any.

shaochuancs
  • 15,342
  • 3
  • 54
  • 62