1

I am building an API using LoopBack 4. Is it possible to use relations as if they were actual properties?

The API stores events (e.g. concerts) in the events table in the database and event dates in the event_dates table. I have successfully added a hasMany relation to the Event model and a belongsTo relation to the EventDate model (one Event can have multiple EventDates) using this [1] instructions.

While I can query the dates using eventRepository.dates(eventId), there aren't available when I request http://localhost:3000/events – How could I achieve this without asking eventRepository.dates(eventId) separately?

On the other hand I would like to POST and PATCH events without posting and patching the event dates separately – Is this possible with a few lines of code?

This is what I need to make the dates field available under /events right now (doesn't seem to be the right way):

const events = await this.eventRepository.find(filter);
for (let event of events) {
   event.dates = await this.eventRepository.dates(eventId).find()
}

When I want to add a new event, I need to do this:

POST /events
POST /events/:id/event-dates
POST /events/:id/event-dates
...

Please note: I'm looking for solutions that are already available within the LoopBack framework. Implementing these things are not the problem, I just want this as short and maintainable as possible.

[1] https://loopback.io/doc/en/lb4/HasMany-relation.html

matthc
  • 13
  • 3

1 Answers1

0

While I can query the dates using eventRepository.dates(eventId), there aren't available when I request http://localhost:3000/events – How could I achieve this without asking eventRepository.dates(eventId) separately?

We call this feature "inclusion of related models" and unfortunately it's not implemented yet. You can track the progress here: https://github.com/strongloop/loopback-next/issues/1352

On the other hand I would like to POST and PATCH events without posting and patching the event dates separately – Is this possible with a few lines of code?

If I understand you correctly, you would like to update both Event and EventDate instance via a single REST API call. We don't support that functionality and to be honest, I am not sure if we ever will.

This is what I need to make the dates field available under /events right now (doesn't seem to be the right way).

You solutions is vulnerable to what's known as "SELECT 1+N PROBLEM" (see e.g. What is the "N+1 selects problem" in ORM (Object-Relational Mapping)?). If your Event has N dates, then you make 1+N database queries.

A better solution is to leverage LoopBack's inq operator:

const eventIds = events.map(e => e.id);
const dates = await this.datesRepository.find({where:{eventId:{inq: eventIds}}});
// copy "dates" entries to relevant "event" items
// by matching "dates[].eventId" against "events[].id"

If the number of queried events is high, then you will need to split eventIds array into smaller chunks and call this.datesRepository.find multiple times.

Anyhow, that's the gist of what we are going to implement in LoopBack in the near future.

Since you are looking for a solution that's already implemented in the framework, the code snippets you have posted are pretty much that.

Miroslav Bajtoš
  • 10,667
  • 1
  • 41
  • 99
  • Thank you very much for your response, I think I can deal with it. – matthc Jun 18 '19 at 12:11
  • You said, that you don't think that LoopBack will ever support updating resources and their relations in a single request – What would be the best practice here? When my form combines the event data and the dates, do I need to make different POST requests to the two different endpoints? When I use `@property.array` instead of `@hasMany` it does all the things I've wished myself. The only thing is ... it stores the related EventDates within a single database field, which is not as clean as I'm aiming for. – matthc Jun 18 '19 at 12:22
  • > When I use `@property.array` instead of `@hasMany` it does all the things I've wished myself. The only thing is ... it stores the related EventDates within a single database field, which is not as clean as I'm aiming for <-- We call this "embedded" relations, I think we will eventually implement them in LB4 too. If you want to store related models in different tables/collections, then I would recommend to modify your controller method to call related repositories to create/update related models. You can also implement this in the per-model repository class too. – Miroslav Bajtoš Jun 18 '19 at 14:57