feathersjs / feathers

The API and real-time application framework
https://feathersjs.com
MIT License
14.97k stars 742 forks source link

Wanted to have a request with query like {$in: [.....]}, but koa-qs has array limit already inside the feathers koa. #3399

Closed arcanys-ivan closed 2 months ago

arcanys-ivan commented 5 months ago

Wanted to have a request with query like {$in: [.....]}, but koa-qs has array limit already inside the feathers koa.

Is there any way to update the limit or remove it after all? Or this feature will be added soon? or not?

Thanks

Originally posted by @arcanys-ivan in https://github.com/feathersjs/feathers/discussions/3312

lvivier commented 5 months ago

I just ran into this limitation as well. qs by default will not parse an array with more than 20 items, it will convert it to an object with numbered indices as keys instead. The query validator rejects this construction and we get a 400 Bad Request with data along these lines:

{
           "instancePath": "/id/$in",
           "schemaPath": "#/properties/id/anyOf/1/properties/%24in/type",
           "keyword": "type",
           "params": {
             "type": "array"
           },
           "message": "must be array"
}

koa-qs supports passing options to qs.parse() since 3.0.0. It would be helpful if @feathersjs/koa either passed { arrayLimit: 0 } into koa-qs or allowed the user to pass in an options object.

Maybe there's another possibility where a koa app that's already had koa-qs applied to it can be passed as the second arg to the koa() fn from @feathersjs/koa?

lvivier commented 4 months ago

fwiw we are working around this with a global hook along these lines:

import { traverse } from 'feathers-hooks-common'
import { isObject } from 'lodash'

const METHODS = ['$in', '$nin', '$ne', '$or', '$and']

export default function queryArrays () {
  return traverse(function (node) {
    if (METHODS.includes(this.key) && isObject(node) && isArrayable(node)) {
      this.update(Object.values(node))
    }
  },
  ctx => ctx.params.query)
}

function isArrayable (obj) {
  return Object.entries(obj).every(([key, value]) => !isNaN(+key) && typeof value === 'string')
}