feathers icon indicating copy to clipboard operation
feathers copied to clipboard

Issues with $sort and createQuery/._find

Open michalgm opened this issue 6 months ago • 3 comments

0This may be more of a breadcrumb for others having this issue than an actual bug report, though perhaps documentation can be improved.

I was trying to modify the incoming query in a service hook, and noticed that the $sort param was not being respected properly when passed into context.service.createQuery. I had similar results using context.service.find. Note that i am using postgres via the feathers-knex provider.

When sending (from the REST client) a query like

          $select: [
            "record_id",
            "title",
          ],
          $limit: PAGE_SIZE,
          $skip: offset,
          $sort: { title: 1 },
        };

and then in my hook, creating a query using context.service.createQuery(context.params) (or with context.service._find(context.params), I noticed that the sort on title was always desc regardless of what I specified in the $sort parameter. After digging around, I found https://github.com/feathersjs/feathers/blob/dove/packages/knex/src/adapter.ts#L153

    if (filters.$sort) {
      return Object.keys(filters.$sort).reduce(
        (currentQuery, key) => currentQuery.orderBy(key, filters.$sort[key] === 1 ? 'asc' : 'desc'),
        builder
      )
    }

in the knex provider code. It seems that integers are converted into strings when passed into the API, so I was ending up with $sort: { title: "1" } which of course was failing the === 1 test.

Upon further digging, I found a pretty acceptable workaround by passing params into the (undocumented) context.service.sanitizeQuery method like so:

const params = {
    ...context.params,
    query: await context.service.sanitizeQuery(context.params)
  };
context.service.createQuery(params);

this results in the $sort param (and perhaps others) being properly interpreted.

I can definitely see why you might not want to pass the query arg to ._find or createQuery through sanitizeQuery by default, but maybe a note could be added to the docs pointing this behavior out?

michalgm avatar May 15 '25 23:05 michalgm

Are you using the REST adapter? This smells like the aged old problem of URL query parameters not being validated. Something as simple as ?order=1 will parse the query parameter as "1", similar for other natives like "true". The REST protocol doesn't specify how to handle various types in the URL. You should use a Feathers schema to validate the query, which will also cast it to its proper types.

DaddyWarbucks avatar May 23 '25 05:05 DaddyWarbucks

Yes,, I am using the REST adapter. Isn't passing the context.params through context.service.sanitizeQuery essentially running the params through the standard schema validator? Is there a better way to run the params through a schema?

michalgm avatar May 23 '25 05:05 michalgm

https://feathersjs.com/api/schema/validators.html#validatequery

DaddyWarbucks avatar May 23 '25 13:05 DaddyWarbucks