Add Fields/Select Param for GET Endpoints #1563
Replies: 3 comments 8 replies
-
@pbn4 and @emilyjablonski , can you review and provide feedback please. @willrlin , I'm curious as to how this or something like this may fit into your team's plans. DHALIA handled this problem on the main listing view by adding a separate endpoint with fewer selects and joins. That is a possibility, but I don't like the idea of adding a bunch of endpoints for every use case when we can have a generic reusable solution. |
Beta Was this translation helpful? Give feedback.
-
The idea looks OK to me. Possible problems:
Regarding the parser: Entity and it's relations represent a tree so we could create an input model from
consider 0 a leaf node. We could build a query by traversing it recursively, so for each level we do not need to keep |
Beta Was this translation helpful? Give feedback.
-
This contrasts a bit with the approach we took for our filters and pagination work, which I'm preparing to contribute upstream to you in #1578 (There's a few frontend fixes I need to make before mailing this PR, which I'll do tomorrow.) As I noted in the PR description, rather than exposing arbitrary fields, we opted to keep the backend opinionated about which fields and filters it exposes to the frontend. This keeps the frontend from having to know anything about the database schema, which seemed preferable to us. The code that actually applies the filters is still generic and could be used for other entities, but only the fields listed in I really like the idea of specifying the fields the frontend actually cares about, instead of requesting everything. What if, instead of the frontend requesting exactly the fields it needs (which would require the frontend knowing about the database schema, which is what we were trying to avoid with our filters strategy) the backend defines several "lenses" or "views" on the entity and the frontend can request the view it wants? Some rough details:
This is more resilient to the database schema changing, and won't require updating every data call from the frontend, just the view definitions. (@willrlin and @anders-schneider for visibility) |
Beta Was this translation helpful? Give feedback.
-
There is a need to specify which columns we want selected and returned for an endpoint. The main /listings endpoint for public for example fetches every field and relation specified in the listing service, which is quite a lot, regardless if it's needed and thus has huge performance implications.
There are of course a number of ways to handle this, so I will start with one idea and we can go from there.
Using swagger's default
style: form
and not defaultexplode: false
for serialization, a simple query would look like:That's pretty straight forward, but what about specifying relationships?
Since we can use
.
in query parameters this makes it easier:Notice that in this example, the
id
is specified in the units and units.unitType relationships. Since a field likeid
is typically required for a join, we can either make it part of our documentation that those fields must also be selected, if you wish to have them returned or we can programmatically add them in if it's not present. For this idea, I think it makes sense to start with the simpler documentation approach.Now to the more complicated side, actually parsing these to add the fields to the query builder. We could create a simple map that relates the fields that come through the parameter to what needs to get joined and selected, or we could handle it more generically. Below are my initial thoughts on how we could do this.
We'll want a function that takes the fields param, query builder (qb) to add the fields to and the default schema, so we can properly add the fields that belong to the entity.
Within addFields, we
fields.split(",")
and loop over it. If the field does not contain a.
, then we can assume that field is on the model, so for example:If
testFieldRegex.test(field) === true
, then we can call another function which traverses the field path, like ["units", "unitType", "name"]. This function will receive the field path, the schema of the field, query builder (qb) and a joins arg, so we can keep track of the joins that have already been added.We can first get the field's schema and the field with
This way, if the field is nested like
units.unitTypes.name
, we can check if joins[relation] is undefined, if so, we can set that and add the join withIn this example, since fields length is greater than one, we would pass fields as the path and relation as the schema back into joinAndSelect. If the fields length === 1, then we can call addSelect.
If the fields param is not present, then it makes sense to default to what we're doing now.
Beta Was this translation helpful? Give feedback.
All reactions