16

Express implements a server side session object that lets you store data specific to a client. How would you do the equivalent in Meteor?

strack recommended using a collection. This would work if the ids of objects in the collection were session_ids that were exposed both server and client side on the connection objects.

It appears the client and server share a session_id via the LivedataConnection on the client:

if (typeof (msg.session) === "string") {
  var reconnected = (self.last_session_id === msg.session);
  self.last_session_id = msg.session;
}

and the LivedataSession object on the server:

self.id = Meteor.uuid();

But the Meteor API doesn't expose these objects. What is the correct way of accessing the session information?

It would be really convenient if a client's Session object synced with a server side Session object unique to the client that is accessible from Meteor#publish and Meteor#methods.

Guilherme David da Costa
  • 2,318
  • 4
  • 32
  • 46
joshrtay
  • 390
  • 1
  • 3
  • 11
  • 1
    +1 on transparent client and server Session syncing. I assumed it worked this way, and was confused it wasn't working – 7zark7 May 16 '12 at 07:31

6 Answers6

8

The user-session smart package I wrote for Meteor is designed exactly for this. It provides all the methods of the Meteor Session API (with the exception of setDefault), and some additional ones as well. It's reactive, and all the changes are persistent. Best of all, it's available both on the client, and on the server with an additional userId argument.

BenjaminRH
  • 11,974
  • 7
  • 49
  • 76
  • 2
    Excellent package. This is what Meteor's Session should be by default. Would also be easier to handle the issue I filed re. publish functions. – Dan Dascalescu Dec 09 '13 at 02:44
  • 1
    That is a great package. Why did you chose to rely on `userId` though? Is it not possible to just rely on a `sessionId`, that would work even without being logged in? I haven't looked at your code yet. I grab some diner and start looking into it to get a better understanding of all this – Alexandre Bourlier Dec 14 '15 at 20:17
7

If you're willing to use the Auth branch of Meteor, this is what I did with some added comments. I wasn't a fan of Josh's answer because I don't trust clients! They lie.

In this example, we'll say that each user has a single magical object. And we refuse to use any information the user can manipulate client side (i.e. session variables).

On Server:

//Create our database
MagicalObjects = new Meteor.Collection("magicalObjects");

// Publish the magical object for the client
Meteor.publish("get-the-magical-object", function () {

//In the auth branch, server and client have access to this.userId
//And there is also a collection of users server side

var uid =  this.userId();
//I make sure that when I make this connection, I've created a magical object 
//for each user. 

//Let's assume this adds a parameter to magical object for the userId
//it's linked to (i.e. magObject.uid = ~user id~ )

//we grab our current user from the users database, and pass to our function
checkUserHasMagicalItem(Meteor.users.findOne({_id: uid}));

var self = this;
console.log('Writing publish');
console.log('uid: ' + this.userId());

var magicalObject = MagicalObjects.findOne({uid: uid});

//Now, I want to know if the magical object is changed -- and update accordingly 
//with its changes -- you might not need this part

//If you don't- then just uncomment these two lines, ignore the rest
//self.set("magicObject", uid, {magicalobject: magicalObject});
//self.flush();

//Here, we're going to watch anything that happens to our magical object
//that's tied to our user
var handle = MagicalObjects.find({uid: uid}).observe({
    added: function(doc, idx)
    {       
    //get the latest version of our object
    magicalObject = MagicalObjects.findOne({uid: uid});
    console.log('added object');
    //now we set this server side
    self.set("magicObject", uid, {magicalobject: magicalObject});
    self.flush();   
    },
     //I'm not concerned about removing, but
    //we do care if it is changed
    changed: function(newDoc, idx, oldDoc)
    {
    console.log('changed object');
    magicalObject = MagicalObjects.findOne({uid: uid});
    self.set("magicObject", uid, {magicalobject: magicalObject});
    self.flush();           
    }       
//end observe

});

//for when the player disconnects
self.onStop(function() {

    console.log('Stopping');
    handle.stop();

//end onStop
});

//end publish
});

On Client:

//this is the name of our collection client side
MagicalObject = new Meteor.Collection("magicObject");

//notice the name is equal to whatever string you use when you call
//self.set on the server

//notice, this is the name equal to whatever string you use when you
//call Meteor.publish on the server
Meteor.subscribe("get-the-magical-object");

Then, when you want to go and grab your magical object:

var magicObject = MagicalObject.findOne().magicalobject;

Notice here that .magicalobject is NOT a typo, it's the parameter we used in self.set -- {magicalobject: magicalObject}.

I apologize for the long answer. But to quickly wrap up: what have we done?

On the server, we have a collection of MagicalObjects which the client doesn't have access to. Instead, we publish a single object from magical objects -- which we call "magicalObject." According to what we set up, each object belongs to one user. So it's a user specific object as requested by the op.

The client creates a collection (whose name is "magicalObject"), and then gets sent the data when the actual data in the server database changes. This collection only has one object by design, but that object can have many parameters (e.g. magicalObject.kazoo or magicalObject.isHarryPotter) or you can store many different objects (e.g. nonMagicItem).

Paul
  • 169
  • 2
  • 10
3

I think a "meteor" way to do this is:

On server side create and publish a ClientSession collection

UserSession = new Meteor.Collection("user_sessions");

Meteor.publish('user_sessions', function (user) {

    return UserSession.find(user);    
});

On client side

Session.set('user_id', 42);

UserSession = new Meteor.Collection("user_sessions");
Meteor.subscribe('user_sessions', Session.get('user_id'));

You now have an application-level UserSession object that is specific to that user that you can put/get stuff.

Also, you can manipulate the UserSession collection on the server using Meteor#methods.

Josh Petitt
  • 9,371
  • 12
  • 56
  • 104
  • How do you keep track of when a user disconnects without polling? – joshrtay Apr 28 '12 at 05:58
  • IDK? What I showed would be a persistent client data (not really any different from any other Meteor Collection. – Josh Petitt Apr 28 '12 at 15:32
  • @joshrtay seems like you'd have to do some kind of polling for a reliable indication of when a user disconnects (assuming you mean closes the window or navigates to another page). See http://stackoverflow.com/a/10274212/156060 – Adam Monsen Mar 06 '13 at 12:57
  • 7
    Using `user_id` as this is not really good. The user could just set the `user_id` session variable to another number to get another result from the collection. The `Meteor.publish` has a [`this.userId`](http://docs.meteor.com/#publish_userId)! – MiniGod Jun 15 '13 at 04:14
  • Also Session.set is not persistent as we would expect. The user refreshes their browser and we'll have no idea who they are. – yourdeveloperfriend Jun 20 '13 at 05:18
1

One thing to note is that UserSession doesn't work for a user who has not logged in at the client. I faced this scenario, since I wanted creation of a new user's data object to be modified before saving to MongoDB. The modification was to add an attribute/field obtained from the URL path of current page (using Iron Route client side route). But I was receiving this error,

"You cannot use UserSession methods when there is no user logged in."

So if your use case is restricted to sharing data between client and server for a logged in user, UserSession package seems to do the job.

Vinay Vemula
  • 3,855
  • 1
  • 21
  • 24
0

A session behaves a little differently than a collection. If you are truly looking for a session based solution, use the Session.set() method to of course set your values, and retrieve them when needed with Session.get().

Nunser
  • 4,512
  • 8
  • 25
  • 37
-1

I think that's what the Session is for in meteor-- To store information needed on the client side.

If you need to pass something to the server, maybe put it in a Meteor collection?:

Cookies = new Meteor.collection("cookies")

Otherwise, just use Session.

strack
  • 574
  • 4
  • 12