5

This is an extension of this question.

In my models, every one requires a companyId to be set on creation and every one requires models to be filtered by the same session held companyid.

With sails.js, I have read and understand that session is not available in the model unless I inject it using the controller, however this would require me to code all my controller/actions with something very, very repetitive. Unfortunate.

I like sails.js and want to make the switch, but can anyone describe to me a better way? I'm hoping I have just missed something.

Community
  • 1
  • 1
Meeker
  • 5,979
  • 2
  • 20
  • 38

3 Answers3

12

So, if I understand you correctly, you want to avoid lots of code like this in your controllers:

SomeModel.create({companyId: req.session.companyId, ...})
SomeModel.find({companyId: req.session.companyId, ...})

Fair enough. Maybe you're concerned that companyId will be renamed in the future, or need to be further processed. The simplest solution if you're using custom controller actions would be to make class methods for your models that accept the request as an argument:

SomeModel.doCreate(req, ...);
SomeModel.doFind(req, ...);

On the other hand, if you're on v0.10.x and you can use blueprints for some CRUD actions, you will benefit from the ability to override the blueprints with your own code, so that all of your creates and finds automatically use the companyId from the session.

If you're coming from a non-Node background, this might all induce some head-scratching. "Why can't you just make the session available everywhere?" you might ask. "LIKE THEY DO IN PHP!"

The reason is that PHP is stateless--every request that comes in gets essentially a fresh copy of the app, with nothing in memory being shared between requests. This means that any global variables will be valid for the life of a single request only. That wonderful $_SESSION hash is yours and yours alone, and once the request is processed, it disappears.

Contrast this with Node apps, which essentially run in a single process. Any global variables you set would be shared between every request that comes in, and since requests are handled asynchronously, there's no guarantee that one request will finish before another starts. So a scenario like this could easily occur:

  1. Request A comes in.
  2. Sails acquires the session for Request A and stores it in the global $_SESSION object.
  3. Request A calls SomeModel.find(), which calls out to a database asynchronously
  4. While the database does its magic, Request A surrenders its control of the Node thread
  5. Request B comes in.
  6. Sails acquires the session for Request B and stores it in the global $_SESSION object.
  7. Request B surrenders its control of the thread to do some other asynchronous call.
  8. Request A comes back with the result of its database call, and reads something from the $_SESSION object.

You can see the issue here--Request A now has the wrong session data. This is the reason why the session object lives inside the request object, and why it needs to be passed around to any code that wants to use it. Trying too hard to circumvent this will inevitably lead to trouble.

Community
  • 1
  • 1
sgress454
  • 24,870
  • 4
  • 74
  • 92
  • that is about as complete and answer as you can get. – Meeker Apr 04 '14 at 14:00
  • Scott - can you point to some docs that shows these blueprints overrides? I'm searching but maybe not hard enough. – Meeker Apr 05 '14 at 15:35
  • Docs are still incomplete for the beta version, but the [answer I linked to](http://stackoverflow.com/questions/22273789/crud-blueprint-overriding-in-sailsjs/22274325#22274325) should get you started. – sgress454 Apr 05 '14 at 15:42
  • Do you think I could just add companyid to the req.params somewhere before the model execution? If so, where do you think i could add that? I guess i'm looking for some sort of request interceptor where I can add companyId. (this might off topic and I should start a new question) – Meeker Apr 05 '14 at 18:16
  • 1
    found a solution. In a policy across all controllers i add the following req.query.companyId = req.session.user.companyId; All the default models will look to the query when creating / filtering. So out of the box everything works with this simple code. Hope this is a valid way. – Meeker Apr 05 '14 at 21:12
0

Best option I can think of is to take advantage of JS, and make some globally accessible functions.

But its gonna have a code smell :(

InternalFX
  • 1,475
  • 12
  • 14
0

I prefer to make a policy that add the companyId inside the body.param like this:

    // Needs to be Logged
    module.exports = function(req, res, next) {
        sails.log.verbose('[Policy.insertCompanyId() called] ' + __filename);

        if (req.session) {
            req.body.user = req.session.companyId; 
                 //or something like AuthService.getCompanyId(req.session);
            return next();
        }

        var err = 'Missing companyId'; 
        //log ...
        return res.redirect(307, '/');
    };
Makah
  • 4,435
  • 3
  • 47
  • 68