I'm learning Loopback and I want to know the best loopback practices. I have a member model based on user default model and a follow model. A member can have many followers.
{
"name": "Member",
"base": "User",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"nickname": {
"type": "string"
}
},
"validations": [],
"relations": {
"messages": {
"type": "hasMany",
"model": "Message",
"foreignKey": ""
},
"followers": {
"type": "hasMany",
"model": "Member",
"foreignKey": "followeeId",
"through": "Follow"
},
"following": {
"type": "hasMany",
"model": "Member",
"foreignKey": "followerId",
"through": "Follow"
}
},
"acls": [
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
],
"methods": {}
}
Here is my Follow model
{
"name": "Follow",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {},
"validations": [],
"relations": {
"follower": {
"type": "belongsTo",
"model": "Member",
"foreignKey": ""
},
"followee": {
"type": "belongsTo",
"model": "Member",
"foreignKey": ""
}
},
"acls": [],
"methods": {}
}
I want the member to be able to fetch its followers but what is the best way to achieve that:
Member with /Members/{id}/followers
(which is currently blocked) ? Should I create a new remote method for member to achieve this? What is the best way to do that ? Should i check the member identity like I did for another method with the accesstoken (example below)?
Professionaldemand.createDemand = function (accessToken, cb) {
//Rejection if not an authenticated User
if (!accessToken) {
return errorUtility.CallbackError("Access Token is not valid", 400, cb);
}
var currentDate = new Date();
Professionaldemand.create({ Acceptation: false, CreationDate: currentDate, memberId: accessToken.userId },
function (err) {
if (err) {
return errorUtility.CallbackError("Professionaldemand creation error", 400, cb);
}
}, cb(null));
};
Method definition:
Professionaldemand.remoteMethod(
'createDemand', {
http: {
path: '/createDemand',
verb: 'post'
},
description: ["Permits client to ask for a professional permission"]
,
accepts: [
{
arg: 'access_token',
type: 'object',
http: function (ctx) {
return ctx.req.accessToken;
}
}
]
}
);
Your model definition and relation look pretty good.
What needs to be better separated is the access control from the business logic.
Access control needs to be enforced with the acls
property inside each model definition. It should not be handled by your remote method, because otherwise you will need to repeat the code for each remote method, and you probably have better things to do than that :)
First, for each model, it is good practice to DENY everything to everyone. It is a good practice to ensure that you're not leaving holes inside your API.
Then, authorize a particular type of user to each specific route or method. It works because very specific acl rule has priority over broad acl rule.
Ex:
Member
all methods
all users
Deny
Member
__addFollowower__
$owner
Allow
This way the remote method Member.addFollower
will only be allowed for the Member $owner, basically the Member himself. He won't be able to add a follower on behalf of someone else.
I want the member to be able to fetch its followers but what is the best way to achieve that. [...] Should I create a new remote method for member to achieve this ?
In this case you don't necessarily need to create a remote method, in fact it would be redundant with your relation-generated method. You can simply call
GET api/Member/:id/followers?access_token=e23saa2ragkuljkh2...
If you don't want to keep track of the :id
client-side, you can also use
GET api/Member/me/followers?access_token=e23saa2ragkuljkh2...
In this case, loopback will use the access token to determine the id of the logged in user, and replace me
by this id.
PS: This works under the condition that you configured loopback to support this.