Hello!
I just finished the Todo-List Tutorial from the documentation and implemented the Model/Repository relation as the tutorial shows, the only diference between it and my project it's that I have been using a local MySQL server from the beginning, so I created my Models based on the MySQL tables.
In my project I have the next relation => A User can have many Courses.
At the first start of the app, when I requested /courses with GET, the endpoint responded correctly with an array of objects (Courses) and inside of each object, another object with the user that created that Course. That was fine because I wanted so to happen.
Now, from the second time I did npm start (With no changes either on the Models or Repositories), when I request /courses with GET, the endpoint only respond with an array of Courses with no data about Users.
This is an strange bug because the endpoint's Example Value and Schema shows the correct structure:
{
/*... Other props from Courses*/
"user": {
"id": 0,
"xp": 0,
"user_type": "string",
"badges": {},
"titles": {},
"username": "string",
"email": "string",
"password": "string",
"user_photo": "string",
"state": "string",
"courses": [
null
]
}
}
I looked for similar situations, but I could not find any!
@nabdelgadir, could you please take a look? Thanks.
More information about this: I also tried running npm run migrate into a new schema for test this bug and it still does not work.
Thanks for reporting this issue. We couldn't reproduce it on our end. For us, the /courses endpoint only returned the user id and not the user object. Perhaps we set up the relations slightly differently. Would you mind giving us a sample repo to reproduce your problem? See https://loopback.io/doc/en/contrib/Reporting-issues.html#loopback-4x-bugs, thanks!
@nabdelgadir That's exactly the bug that I found! /courses does not return the user object although Swagger UI shows that object in the Example Value section.
The funny thing is /courses returns the user object at the first npm start after the repository/model/controler configuration. After that first start, the endpoint only returns the user id
Sure, I鈥檒l work on a sample repo to try to reproduce this ;)
@gonzarascon Thank you! And that's very strange. Using the explorer, it showed us the schema with the user object but it just returned the id too. The only difference is we never saw the object being returned the first time after running npm start.
Hey @nabdelgadir ! I started to work on the repository for reproducing this bug using the todo-list example and guess what? After running npm start (with no modifications on the code), and requested GET to the /todos endpoint, it only returns the Todo-List id instead of the Todo-List object!
I'm suspecting it could be a problem with the @hasMany and @belongsTo decorators, maybe?
I'll try riding off all cached data on my PC in case there's a problem with that.
@gonzarascon I see where the confusion is coming from. So specifically for the TodoList example, we introduced temporary inclusion so that the schema currently includes the todoList object if you include the filter {include: [{relation: 'todo-lists'}]} when doing GET /todos (see https://github.com/strongloop/loopback-next/pull/3171 if you're interested).
However, in your case, we still don't support inclusion of related models so you won't see the User object when doing GET /courses. We actually currently have a SPIKE open (https://github.com/strongloop/loopback-next/pull/3387) for this feature.
But, if you need inclusion now, you can hardcode it similar to how we did it in our TodoList example. See the find and findById functions in todo.repository.ts and todo-list.repository.ts.
Let me know if you have any other questions. 馃槃
@nabdelgadir oh, I see... But, when I ran the TodoList example, the relations didn't work either. Could it be some sort of problem with loopback caching data that is replicating the issue in other projects?
In my project I copied the harcoded relation from the TodoList (doing the proper modifications to work with my models, respositories and controllers) as I understood that this feature isn't yet supported. It's strange that, the first time I did npm start the relations worked as expected, but after that they seem to be lost or something although the code wasn't changed 馃
@gonzarascon Did you remember to use the filter when doing your GET requests e.g. {include: [{relation: 'todo-lists'}]}? And yes, I agree it's strange you got it the first time. I don't know why it happened, but I hope someone else can chime in if they know how that was possible.
@bajtos @strongloop/sq-lb-apex
@nabdelgadir Do you have any example on how to implement {include: [{relation: 'todo-lists'}]} ? At the moment I copied, as the TodoList example shows, getModelSchemaRef(TodoList, {includeRelations: true}) inside the @get decorator for /todo-lists.
I looked for {include: [{relation: 'todo-lists'}]} inside of the TodoList example and only found that code in the _tests_ folder.
@gonzarascon {include: [{relation: 'todo-lists'}]} is a filter, so when you do the request, you can include it in the url e.g. http://127.0.0.1:3000/todos?filter[include][][relation]=todoList. If you want it by default, you can modify the find method. This is our current one in the TodoList example (in todo.repository.ts) (sorry about the formatting):
async find(
filter?: Filter<Todo>,
options?: Options,
): Promise<TodoWithRelations[]> {
// Prevent juggler for applying "include" filter
// Juggler is not aware of LB4 relations
const include = filter && filter.include;
filter = {...filter, include: undefined};
const result = await super.find(filter, options);
// poor-mans inclusion resolver, this should be handled by DefaultCrudRepo
// and use `inq` operator to fetch related todo-lists in fewer DB queries
// this is a temporary implementation, please see
// https://github.com/strongloop/loopback-next/issues/3195
if (include && include.length && include[0].relation === 'todoList') {
await Promise.all(
result.map(async r => {
// eslint-disable-next-line require-atomic-updates
r.todoList = await this.todoList(r.id);
}),
);
}
return result;
}
You can simplify it to the following so you don't need the filter and it includes them by default:
async find(
filter?: Filter<Todo>,
options?: Options,
): Promise<TodoWithRelations[]> {
// Prevent juggler for applying "include" filter
// Juggler is not aware of LB4 relations
filter = {...filter, include: undefined};
const result = await super.find(filter, options);
// poor-mans inclusion resolver, this should be handled by DefaultCrudRepo
// and use `inq` operator to fetch related todo-lists in fewer DB queries
// this is a temporary implementation, please see
// https://github.com/strongloop/loopback-next/issues/3195
await Promise.all(
result.map(async r => {
// eslint-disable-next-line require-atomic-updates
r.todoList = await this.todoList(r.id);
}),
);
return result;
}
Let me know if this helps. 馃槃
@nabdelgadir thank you very much! So far, I tried all three options: including the filter in the url, modifying the find method with filter and modifying the find method without the filter.
The one that worked for me (in the meantime at least), was including the filter in the url. Although I see this option as the most uncomfortable of all, it's the only that works without problem and I'm going to explain why:
In my case I need the API to return any relations that the current endpoint I'm doing GET has. For example: /courses has a relation with /users (a course belongs to an user), so I need to have an user object in every course object that I'm getting through GET (the endpoint /courses returns an array of courses objects). At the same time, I need to have a courses object when doing GET to /users.
So, if I had a filter as the todoList example shows, the API won't return any relations after the first npm start. Removing the filter (as @nabdelgadir showed above) worked just fine, but I couldn't remove the filter in both repositories as, when doing GET to /courses, the find method at /users will also fetch for /courses entering in a loop.
In conclussion to this brief explanation, I'll stick to the URL method for now and I hope the relations feature to be supported soon!
Thank you @nabdelgadir for all your help!
@gonzarascon Thank you for the explanation! I'm going to close this issue, but if you have any further questions, feel free to ask. 馃槃