Loopbackjs Querying related model via rest api - node.js

is there any way to query related model's related model like from post \Get:
{"order": "created_at DESC","include":[{"relation": "user"}]
But, in my user model there is a relation hasone with settings model. I want to get that also, while querying from post \Get rest api. I've tried with:
{ "include": { "relation": "user","include": {"relation":"settings"}}}
but no luck.

I've create nested relationship to related to your question.
example : teamRole.json
TeamRole > belongTo > User and Team
"validations": [],
"relations": {
"team": {
"type": "belongsTo",
"model": "Team",
"foreignKey": ""
},
"user": {
"type": "belongsTo",
"model": "User",
"foreignKey": ""
}
}
Retrieve results
app.models.TeamRole.findOne({
where: {
userId: user.id
},
include:[ {
relation: 'team'
},
{
relation: 'user'
} ]
},function(err,team,user){
//retrieve relational data here
});
Try this approach, hope this will helpful.

Related

Access Token is not generating when extending User and Access Token Model - Loopback 3

I am new to loopback. So as a part of the learning process, started creating a sample API. Also extended User model as Customer and AccessToken as CustomerAccessToken. But the problem is that access token is not being generated, when we tried to login data that is already send using a POST request.
Response body when login using credentials:
{
"id": "oiMDjErGGVkeSMtnt1SfHzGuERZf6OCId5FUulvir6A04htUbIV656FOBlXn9vDS",
"ttl": 1209600,
"created": "2019-05-09T09:41:05.184Z",
"userId": "5cd3f59594d45186b411bb02"
}
No accessToken is generated. I have removed the inbuilt User and AccessToken model and used extended ones.
model-config.json
{
"CustomerAccessToken": {
"dataSource": "db",
"public": false
}
"Customer": {
"dataSource": "db",
"public": true
}
}
customer.json
"relations": {
"accessTokens": {
"type": "hasMany",
"model": "CustomerAccessToken",
"foreignKey": "userId",
"options": {
"disableInclude": true
}
}
}
customerAccessToken.json
{
"name": "CustomerAccessToken",
"base": "AccessToken",
"properties": {},
"validations": [],
"relations": {
"user": {
"type": "belongsTo",
"model": "Customer",
"foreignKey": "userId"
}
},
"acls": [],
"methods": {}
}
One more query: Will login functionality work, when we try to extend User & AccessToken models or Do I need to write login functionality in the customer.js file in order to make it work.
Any help would be really appreciated.
Try to use the DEBUG variable to get more details about the error. from loopback root call
export DEBUG=loopback:security:access-context
npm start
to run the server in the debug mode. Then you can check what exactly is going on - which method is called and why you don't have access.
Probably it's ACL setting: by default, you cannot get users data in loopback, so you may want to add ACL rule to the Customer entity. I suggest to use loopback-cli for that - it's less error-prone. Then you select a model where you want to add a new rule and following the step-by-step instruction. You can read more about ACL here
Do not use CustomerAccessToken unless you want to implement multiple user models.
This is the configuration that works great for me:
model-config.json
{
"User": {
"dataSource": "mysql",
"public": false
},
"AccessToken": {
"dataSource": "mysql",
"public": false,
"relations": {
"Customer": {
"type": "belongsTo",
"model": "Customer",
"foreignKey": "userId"
}
}
},
"Customer": {
"dataSource": "mysql",
"public": true
}
}
server.js
app.use(loopback.token({
model: app.models.accessToken,
currentUserLiteral: 'me'
}))
Customer model must extend the User model:
customer.json
{
"name": "Customer",
"plural": "customers",
"base": "User"
}
In addition to Antonio Trapanis response. If you want to use your own AccessToken model to have to set it in server.js
app.use(loopback.token({
model: app.models.CustomerAccessToken,
}));
I followed this tutorial.
https://youtu.be/Jx39u8IssRg
I received Token in response body when logged in with 'user' through user controller.
I am able to to hit the end points of to-do model by passing that 'token' in the headers as 'Bearer Token'. I used "postman" to hit the to-do end points
( I was unable to insert/attach the received token into the to-do requests of loopback4 app)

how to store string as an objectId using loopback

I have a Loopback model of project.json
project.json
"properties" : {
"users": {
"type": [
{
"user_id": "string"
}
]
}
}
array of id stored string format
{
"users" : [
{
"user_id" : "5ae319e8ac5718155ca719d0"
},
{
"user_id" : "5ae31a4d770afb158ef6c048"
}
]}
how to store string as an objectId like this
{
"users" : [
{
"user_id" : ObjectId("5ae319e8ac5718155ca719d0")
},
{
"user_id" : ObjectId("5ae31a4d770afb158ef6c048")
}
]
}
In Loopback 4 the string can be stored as ObjectId as follow:
Define this property in your loopback model file
#property({
type: "string",
mongodb: { dataType: "ObjectId" }
})
mainId?: string;
In Project Model
"relations": {
"user": {
"type": "belongsTo",
"model": "user",
"foreignKey": "id"
}}
In User Model
"relations": {
"projects": {
"type": "hasMany",
"model": "project",
"foreignKey": "project_user_id"
}}
If there is no relations, You can use
const ObjectId = require('mongodb').ObjectId;
var userId=ObjectId("5ae319e8ac5718155ca719d0")
and create into db.
You can check the type using typeof(userId). It will be an object.
You should consider to define it as a relationship, not a list of objects. Then you can define relation like users, or members. I assume that a project has many users, and a user can belong to many projects. Then, you need to define hasAndBelongsToMany relationship:
Project.hasAndBelongsToMany(User);
User.hasAndBelongsToMany(Project);
You can read more about it here.

Loopback - how to get id of user and insert into related model? (hasMany)

What I am trying to figure out is how to get the id of the current authenticated user and use that when creating records in the DB as a foreign key of a different model?
To be more specific I need to get the id of the current authenticated user (model: CommonUser) and use that id as a FK when creating a new Event.
The relationships:
I have created a Model based on the User model called CommonUser. Common user has many Events. Event belongs to Common User.
So Event has a foreignKey called commonUserId.
How do I get the id of the user and use that when doing the insert?
I would have thought this would be automatic as part of the process as far as setting up relationships is concerned? Is that incorrect?
Also to complicate matters I have an Event Look-Up table (i will worry about this next so don't feel obligated to dive to deep) because Event also hasAndBelongsToMany through Event Lookup.
User
{
"name": "CommonUser",
"base": "User",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {},
"validations": [],
"relations": {
"events": {
"type": "hasMany",
"model": "Event",
"foreignKey": "eventId",
"through": "EventLookUp"
},
"friends": {
"type": "hasMany",
"model": "CommonUser",
"through": "Friend",
"foreignKey": "friendId"
}
},
"acls": [],
"methods": {}
}
Event
{
"name": "Event",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string"
},
"radius": {
"type": "number",
"default": 50
},
"status": {
"type": "number"
},
"location": {
"type": "geopoint",
"required": true
}
},
"validations": [],
"relations": {
"owner": {
"type": "belongsTo",
"model": "CommonUser",
"foreignKey": "commonUserId"
},
"commonUsers": {
"type": "hasAndBelongsToMany",
"model": "CommonUser",
"foreignKey": "ownerId",
"through": "EventLookUp"
},
"galleries": {
"type": "hasOne",
"model": "Gallery",
"foreignKey": ""
},
"photos": {
"type": "hasMany",
"model": "Photo",
"foreignKey": "",
"through": "Gallery"
}
},
"acls": [],
"methods": {}
}
Event Lookup
{
"name": "EventLookUp",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
If I can be pointed in the right direction that would be fantastic. It's hard to find an answer reading through the documentation. I think I need to use an operation hook before insert and set the Event Models properties? What is loopback's best practice as far as this goes?
In loopback swagger when you login as a user using loopback's default users/login api , you get access token object as response.You can copy the id of access token and paste into the box in top right corner in swagger and set the access token.Thus internally your accesstoken is set in loopback and for your every request from swagger, loopback append the access token along with the request.In this way you can get the access token from ctx(context) in remote methods.
For create, findOrCreate, save an event obj:
Event.observe('before save', function updateUserId(ctx, next) {
let userId = ctx.options.accessToken.userId;`
if (ctx.instance) {
ctx.instance.commonUserId = userId;
}
next();
});
For updateAttributes for event obj:
Event.observe('before save', function updateUserId(ctx, next) {
let userId = ctx.options.accessToken.userId;
if (ctx.currentInstance) {
ctx.currentInstance.commonUserId = userId;
}
next();
});
I found a solution but I am unsure if it is best practice or if there is a better way to handle this.
Anyways I created an Operation Hook inside "/common/models/event.js" and appearently you can get access to the context within this hook.
If there is a better way to do this I would like to know.
'use strict';
module.exports = function (Event) {
Event.observe('before save', function updateForeignKeyCommonUserId(ctx, next) {
// console.log(ctx.options);
/* WHAT Options Looks Like */
// { accessToken:
// { id: 'Z20R1RsnumWEdDzR3TyCCbmZ0DTp4tOh2cviU6JGsrlNIYCs3KchQ7mdAnhTc1VQ',
// ttl: 1209600,
// created: 2018-06-26T17:41:28.298Z,
// userId: 3 },
// authorizedRoles: {} }
// console.log("THE USER ID IS: ");
// console.log(ctx.options.accessToken.userId); // Current User Id
var userId = ctx.options.accessToken.userId;
// So appearently "the context provides either an instance property or a pair of data and where properties"
// #todo: find out why there can be a 'instance' or 'data'.
if (ctx.instance) {
ctx.instance.commonUserId = userId; // instance
} else {
ctx.data.commonUserId = userId; // data
}
next();
});
};
If someone wouldn't mind explaining where context comes from and how it gets populated that would be awesome!

Two relations from same model loopback not working

I have two models users and appointments.
The users model is like below-
{
"users": {
"0": {
"id": "1",
"name": "test1",
"role": "doctor"
},
"1": {
"id": "2",
"name": "test2",
"role": "patient"
},
"2": {
"id": "3",
"name": "test3",
"role": "support"
}
}
}
Now in the above model , if the role is doctor , we are calling it as doctor_id , if patient then patient_id and so on.
Now my appointment model is below->
{
"name": "appointments",
"plural": "appointments",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"appointmentDate": {
"type": "date"
},
"appointmentTime": {
"type": "string"
},
"doctorId": {
"type": "string"
},
"patientId": {
"type": "string"
}
},
"validations": [],
"relations": {
"Doctor": {
"type": "belongsTo",
"model": "users",
"foreignKey": "doctorId"
},
"Patient": {
"type": "belongsTo",
"model": "users",
"foreignKey": "patientId"
}
},
"acls": [],
"methods": {}
}
So when i try to GET all appointments , it is not sending the relation data from users. If i add single relation it work as expected , but not working with multiple relations from same model.
Thanks in advance,
Including my previous comment to give context:
The way I did it, I believe foreignkey should be "" for both. You should not define doctorId and patientId and you probably have to define two hasMany relations in you user class with foreign key "Doctor" and "Patient"
To give an example here are the relations defined in my user class (called customer)
"commentsWritten": {
"type": "hasMany",
"model": "comment",
"foreignKey": "sourceCustomerId"
},
"commentsReceived": {
"type": "hasMany",
"model": "comment",
"foreignKey": "targetCustomerId"
},
then in my comment definition I have the following
"properties": {
...
"targetCustomerId": {
"type": {
"required": true
}
},
"sourceCustomerId": {
"type": {
"required": true
}
}
},
"relations": {
"targetCustomer": {
"type": "belongsTo",
"model": "customer",
"foreignKey": ""
},
"sourceCustomer": {
"type": "belongsTo",
"model": "customer",
"foreignKey": ""
}
},
Please note, that I did define the properties (ids) but, if I remember correctly, it was only so I could force them to be not null, i.e. you should not need it.
If you go to the belongsTo documentation you can see that the foreignKey field is empty, so you need to remove it from your relations definition.
You probably also want to define the other side of the relation.
I.e. define a hasMany relation users hasMany appointments:
{
"name": "users",
"base": "PersistedModel",
...
"relations": {
"doctors": {
"type": "hasMany",
"model": "appointments",
"foreignKey": "doctorId"
},
"patients": {
"type": "hasMany",
"model": "appointments",
"foreignKey": "patientId"
},
...
}
However, what you are trying to do might not be supported (and in fact I'm not even sure if it makes sense for LoopBack).
You can check the documentation on polymorphic relations and even though it's a work in progress there's no mention of having Model A hasMany Model B through more than one foreignKey at the same time.
LoopBack would need to have some logic to search under the first foreign key and if nothing is found then searching under the other foreign key, and I'm not sure if this complex composition is supported.
Suggestion
Why not define two models, one for Doctors and one for Patients and have two different relations instead? You can even go to the documentation on HasManyThrough relations and you can see an example that models something very similar to what you are trying to do.

Include sub model in related model in loopback

I have Account model which hasMany Posts, and Post belongsTo Account
Account:
{
"name": "Account",
"base": "User",
"relations": {
"post": {
"type": "hasMany",
"model": "Post",
"foreignKey": "accountId"
},
...
},
...
}
Post:
{
"name": "Post",
"base": "PersistedModel",
"relations": {
"account": {
"type": "belongsTo",
"model": "Account",
"foreignKey": ""
}
},
...
}
Now, I have model Question which is sub model of Post model.
{
"name": "Question",
"base": "Post", ...
}
I want to query for specific Account all his fields and to include all his Questions with something like this
Account.findById({
id: id,
filter: {include: 'question'},
function(){...});
Is it possible?
Account.findById(id, { include: { relation: 'questions' } }, function(){...});
You may need to create a questions relationship in the Account model, as I don't think it will inherit those from your Post model.
Note also that you should probably rename the post relationship to posts. So your relations section should look like:
Account:
{
"name": "Account",
"base": "User",
"relations": {
"posts": {
"type": "hasMany",
"model": "Post",
"foreignKey": "accountId"
},
"questions": {
"type": "hasMany",
"model": "Question",
"foreignKey": "accountId"
}
...
},
...
}

Resources