Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way to add a friend route in node.js and mongoose?

I'm planning to to create a route where a user could add another user as his/her friend, so that they could chat to each other once they are friends.

So basically once User A has sent a request to User B, User B will get a live notification about the request via socket.io

The problem right now is that, I couldn't come up with my own solution on how to implement the above scenario, from what I know, I should create two routes GET and POST

I'm using mongoose for database query, insert , update and delete

Here's my code

// GET route for getting the user's information -- Simple route

router.get('/users/:facebook_name', function(req, res) {
  User.findOne(facebook_name, function(err, user) {
    if (!user) {
      res.json({message: "Couldn't find a user by that name"});
      return;
    } 
    res.json(user);
  });
});

// POST route for adding a friend 
router.post('/friendships/create/:facebook_name', ensureAuthenticated, function(req, res) {
  // What should i put in this route to make the adding a friend feature works?
  User.findOneAndUpdate(facebook_name, function(err, user) {  
    if (user) {
      res.json({message: "You already send a friend request to that person"});
      return;
    } 
    // Send a live notification to the other user
    socket.emit('sending request', {message: "added you as a friend"});
  }); 
});

user Schema code -- Not really sure about this one either

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var UserSchema = new Schema({

  friends: [{ type: Schema.Types.ObjectId, ref: 'User'}],
  facebook: {
    id: String,
    token: String,
    // email: String,
    displayName: String,
    photo: String
  }
});

// Should I even create this schema?
var FriendsRequest = new Schema({

  madeBy: [{ type: Schema.Types.ObjectId, ref: 'User'}],


})

module.exports = mongoose.model('User', UserSchema);
module.exports = mongoose.model('FriendsRequest', FriendsRequest);

I'm not entirely honest with you guys, in the POST route, i have no freaking idea on how to write the logic, because I'm really confuse right now, how the User B gonna get the live request notification? Should i create another route for that?

This is my problem when it comes to building slightly complex apps , i just couldn't come up with a good logic on how to do a certain feature even though it looks pretty easy. I've been stuck in this problem for almost 4 hours, browsing and reading the net, but I believe SO is the only place for me to find a clue on how to do something.

Thank you.

like image 993
sinusGob Avatar asked Sep 01 '15 11:09

sinusGob


2 Answers

What you can do is create socket for each facebookName(if unique).

On Client Side:

socket.on('connection', function (data) {
   socket.emit('setFacebookName', facebookName); });
}

Server saves each socket with facebookName:

socket.on('setFacebookName', function (facebookName) {
    users[facebookName]=socket;
});

Now, when user sends chat request to that user in this request

// POST route for adding a friend 
router.post('/friendships/create/:facebook_name', ensureAuthenticated, function(req, res) {
  // What should i put in this route to make the adding a friend feature works?
  User.findOneAndUpdate(facebook_name, function(err, user) {  
    if (user) {
      res.json({message: "You already send a friend request to that person"});
      return;
    } 
    // Send a live notification to the other user
    sendLiveNotification(facebook_name);
  }); 
});

function sendLiveNotification(facebookName){
   socket.on('send notification', function (facebookName) {
     users[facebookName].emit('sending request', "has sent friend request");
   });
}
like image 173
Literally Illiterate Avatar answered Nov 10 '22 06:11

Literally Illiterate


You're trying to get a two step process, so you will need at least two calls where one is a request from the requester, and the other is the decision whether or not to allow that request from the requestee. You can handle your callback for the first function utilizing a Boolean where if it's a new request the user could be prompted with a popup on the client.

A good purpose of Mongoose is the extensions to the Schema that you can make, so here I'm adding two functions: one from the requester requesting requestee's friendship, and the other the decision of the requestee

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var UserSchema = new Schema({

  friendsAccepted: [{ type: Schema.Types.ObjectId, ref: 'User'}],
  friendsRequested: [{ type: Schema.Types.ObjectId, ref: 'User'}],
  friendsPending: [{ type: Schema.Types.ObjectId, ref: 'User'}],
  friendsRejected: [{ type: Schema.Types.ObjectId, ref: 'User'}],
  facebook: {
    id: String,
    token: String,
    // email: String,
    displayName: String,
    photo: String
  }
});



UserSchema.statics.requesterInitiatedRequestForFriendship = function(requesterID, requesteeID, cb) {
    mongoose.model('UserSchema').findOne({_id: requesterID}).exec(function(err, requester) {
        if (err) return cb(err);
        mongoose.model('UserSchema').findOne({_id: requesteeID}).exec(function(err, requestee) {
            if (err) return cb(err);
            if (requestee.friendsAccepted(requesterID) === -1 &&
                requestee.friendsRequested(requesterID) === -1 &&
                requestee.friendsPending(requesterID) === -1 &&
                requestee.friendsRejected(requesterID) === -1) {
                requestee.friendsPending.push(requesterID);
                requester.friendsRequested.push(requesterID);
                requestee.save();
                requester.save();
                cb(null, true);
            } else {
                cb(null, false);
            };
        });
    });
};

UserSchema.statics.requesteeDecidedOnFriendship = function(requesterID, requesteeID, allowed, cb) {
    mongoose.model('UserSchema').findOne({_id: requesterID}).exec(function(err, requester) {
        if (err) return cb(err);
        mongoose.model('UserSchema').findOne({_id: requesteeID}).exec(function(err, requestee) {
            if (err) return cb(err);
            if ((requestee.friendsAccepted(requesterID) === -1 &&
                requestee.friendsRequested(requesterID) === -1 &&
                requestee.friendsPending(requesterID) > -1 &&
                requestee.friendsRejected(requesterID) === -1) &&
                requester.friendsRequested(requesteeID) > -1) {
                requestee.friendsPending.forEach(function(uid, idx) {
                    if (uid === requesterID) {
                        requestee.friendsPending.splice(idx, 1);
                        return;
                    };
                });
                requester.friendsRequested.forEach(function(uid, idx) {
                    if (uid === requesteeID) {
                        requester.friendsRequested.splice(idx, 1);
                        return;
                    };
                });
                if (allowed) {
                    requestee.friendsAccepted.push(requesterID);
                    requester.friendsAccepted.push(requesteeID);
                } else {
                    requestee.friendsRejected.push(requesterID);
                    requester.friendsRejected.push(requesteeID);
                }
                requestee.save();
                requester.save();
            };
            cb(null);
        });
    });
}


module.exports = mongoose.model('User', UserSchema);

So a couple things happening:

  • hasn't been tested
  • it's not DRY
  • it's limited without an additional Friendship Schema

With a Friendship Schema, you can define levels of rejection (e.g. "not at this time", etc.), you can more well flush out details and granular control for the changing behavior of friendships. In the above, you can see that once you're rejected, it's pretty fatalistic in that it's determined at no time you shall become friends! So to get more of that type of behavior, I'd definitely go with a Friendship Schema with it's statics and methods flushed out, as should be users.

like image 22
aug2uag Avatar answered Nov 10 '22 07:11

aug2uag