Get only selected fields over rest api

default discord avatar
arctomachine
5 months ago
20

I have collection for doctors, they include many different fields, some of which a big in size. So when I run query to fetch them all, response size becomes scary.


However, logic that uses it only requires few small fields, leaving most of response data unused. Is it possible to transform query to get those few fields instead of whole document in rest api?


I want to avoid using graphql because running gql client on ssr/ssg frontend is too complex, and contents of queries are only changed manually in code, if at all



The problem is actually a little deeper, as each document pulls another related collection. And also one more collection, which in turn pulls that second collection



  • default discord avatar
    philiposaurus
    5 months ago

    The rest api exposes a depth query parameter. That controls how many levels of related documents should be retrieved. Maybe that'll work in your case?


    https://payloadcms.com/docs/getting-started/concepts#depth
  • default discord avatar
    arctomachine
    5 months ago

    I thought about it. It can decrease size to certain degree, but still far from what selective query would achieve

  • default discord avatar
    tinouti
    5 months ago

    You might have to go the custom endpoints route where you'll be able to make your own graphql queries.


    Either that or do some pre-processing and cleanup using the

    beforeRead

    hook.

  • default discord avatar
    arctomachine
    5 months ago

    How would I proceed with custom endpoints if I only need id and slug from related collections?

  • default discord avatar
    tinouti
    5 months ago

    I unfortunately won't be able to help much here as I haven't yet had the opportunity to play with either custom endpoints or graphql queries, but the docs would be a good place to start. 😊

    https://payloadcms.com/docs/rest-api/overview#custom-endpoints
  • default discord avatar
    philiposaurus
    5 months ago

    I have done something along these lines, it would look a little somthing like:


          {
            path: '/my-endpoint',
            method: 'get',
            handler: async (req, res) => {
              const queryResult = await payload.find({
                collection: 'collection-name',
                limit: 0, // 0 gets all
              });
      
              if (queryResult.docs.length === 0) {
                res.status(404).json({ message: 'Nothing found' });
                return;
              }
    
              const cleanedDocs = queryResult.docs.map((doc) => {
                // Do something with each doc, like getting only the properties you need
                return {
                  property1: doc.property1,
                  property2: doc.property2,
                };
              });
      
              res.status(200).json({
                ...queryResult,
                docs: cleanedDocs,
              });
            }
          }
  • default discord avatar
    arctomachine
    5 months ago

    That looks good, I will try it

  • default discord avatar
    Teun
    5 months ago

    You can also use authentication for this. If a certain type of user never needs to see these fields, You just don't give them read access to those fields.



    If you do go for the custom endpoint, you don't need to use that

    limit: 0

    thing, but instead you can just pass the pagination query parameters on to the find function.

  • default discord avatar
    arctomachine
    5 months ago

    I need that limit 0. If I could paginate, response size would not be much problem, probably



    I found one problem with custom endpoints. They pull drafts when they are not supposed to. My access setting only allows getting published documents from api, yet custom endpoint pulls drafts. I also specified

    draft: false

    in .find options, but still get drafts.

  • default discord avatar
    tinouti
    5 months ago

    That's odd! Do you mind sharing your code / query? 👀

  • default discord avatar
    arctomachine
    5 months ago

    filtering for _status serves drafts too



    const queryResult = await payload.find({
              collection: 'doctors',
              limit: 0,
              where: {
                ['_status']: { equals: 'published' },
              },
            })
  • default discord avatar
    tinouti
    5 months ago

    Are you sure about that

    ['_status']

    syntax (vs just

    '_status':

    )? 🤔

  • default discord avatar
    arctomachine
    5 months ago

    Probably. It is normal styntax for objects



    object['some strange property name']

  • default discord avatar
    tinouti
    5 months ago

    Oh okay, I wasn't familiar with that syntax within an object definition. 👍


    It's pretty odd either way though. According to the docs:


    If you simply fetch your created document using a find or findByID operation, your published document will be returned and the drafts will be ignored.

    So by default, a simple

    .find()

    should not return drafts unless

    draft: true

    is specified. Weird... 🤔



    And you're 100% sure that it's actually returning drafts, right? Like the docs returned have the

    _status: draft

    property?

  • default discord avatar
    arctomachine
    5 months ago

    Yes. I added js filter just before mapping array and only included published, with this everything works, but with previous attempts frontend build failed exactly because this query returns drafts and default endpoint fails to fetch this draft

  • default discord avatar
    Teun
    5 months ago

    Hi @arctomachine , I've written a plugin called [payload-query](

    https://www.npmjs.com/package/payload-query

    ) that provides you with exactly what you're looking for.


    It includes a simple plugin that adds the ability to pass a

    select

    query parameter into any Payload rest endpoint.


    You can use it like this:


    http://localhost:3000/api/posts?select[color]=true


    You can also use it to omit some fields instead of selecting, if that's more convenient.

  • discord user avatar
    jmikrut
    Payload Team
    5 months ago

    this is because local api bypasses access control by default



    you can opt back into access control being enabled / functioning normally via

    overrideAccess: false


    in any local api operation



    also worth noting for everyone else in this thread, a

    select

    arg has been asked for quite a bit, and we have been hesitant to implement it due to the fact that graphql supports this / is purpose-built for this reason, but we have been getting such consistent questions about this feature that we are now thinking we should probably support it



    there is a PR open that gets us most of the way there

  • default discord avatar
    arctomachine
    5 months ago

    I tried this option and got even more strange results. Even if request sends correct api token, console gives forbidden error, then dev server crashes.

  • discord user avatar
    jesschow
    Payload Team
    2 months ago

    Hey @arctomachine how are you getting on here? do you still need help?

  • default discord avatar
    arctomachine
    2 months ago

    I solved it with custom endpoints. Not optimal, but it works. But if there was better way introduced in meantime, I will look into it too

Open the post
Continue the discussion in Discord
Like what we're doing?
Star us on GitHub!

Star

Connect with the Payload Community on Discord

Discord

online

Can't find what you're looking for?

Get help straight from the Payload team with an Enterprise License.