Exclude field when fetching a record

default discord avatar
Lloyd
7 months ago
55

Hello, I'm trying to fetch a record from my frontend (nextjs) and I want to exclude a field from the data that PayloadCMS will return. In this example I will exclude the ID fields of the record. This is my code:


    if (courseIdState && courseTierState) {
      ;(async function () {
        const queryForManualSimulations = {
          and: [
            {
              'coursesWithAccess.course': {
                equals: courseIdState,
              },
            },
            {
              'coursesWithAccess.tier': {
                equals: courseTierState,
              },
            },
          ],
        }

        const stringifyQueryForManualSimulations = qs.stringify(
          { 
            where: queryForManualSimulations,
            select: '-id' ,
          },
          { addQueryPrefix: true },
        )

        const reqManualSimulations = await axios.get(
          `/backend/api/manual-simulations${stringifyQueryForManualSimulations}`,
        )

      })()
    }

It returns me the record I'm looking for successfully, but it also includes its ID, which I want to exclude from the result. Is there any way to exclude a specific field when fetching a record from PayloadCMS? Thanks!



PS: in the URL, I have a NextJS rewrite that assigns to

/backend

the URL of PayloadCMS

  • default discord avatar
    notchris
    7 months ago

    Hello @Lloyd! You can set access control on certain fields so that they excluded from the returned record



    import { CollectionConfig } from 'payload/types';
    
    const Posts: CollectionConfig = {
      slug: 'posts',
      fields: [
        {
          name: 'title',
          type: 'text',
          access: {
            create: ({ req: { user } }) => { ... },
            read: ({ req: { user } }) => { ... },
            update: ({ req: { user } }) => { ... },
          },
        };
      ],
    };


    https://payloadcms.com/docs/access-control/fields


    For instance, if you want to prevent the field from being included in the request, return a boolean from the function on 'read'



    read: ({ req: { user } }) => { return false }


    Another way would be to create a custom endpoint with the local API, but I think access control should be what you want here



    In addition, if you want more fine grain control, a common pattern is to set roles on the 'user' collection, so that you can refine the access controls on fields to specific groups.

  • default discord avatar
    Lloyd
    7 months ago

    Hi, thank you for the response. I need to exclude the ID field only for the request on one of my pages in NextJS, but I need the field to be included in the request I make from another page. As I said in the post above, I used the

    id

    field only as an example, but in the real website I want to exclude an array that will contain a lot of info, so I can save some bandwidth on the page that doesn't need that additional info, but still be able to fetch this info on another page that needs it.


    Can I selectively exclude fields from the request, or do you suggest to write a custom endpoint for it?

  • default discord avatar
    notchris
    7 months ago

    @Lloyd Ah I see now. Are you familiar with Express? Payload uses express in the background to manage endpoints



    In your

    server.ts

    file, you can add a route to your app



    (Which is an express app)



    And then you can do something like



    (inside the route)



    const result = await payload.find({
      collection: "posts", // required
      depth: 2,
      page: 1,
      limit: 10,
      where: {}, // pass a `where` query here
      sort: "-title",
      locale: "en",
      fallbackLocale: false,
      user: dummyUser,
      overrideAccess: false,
      showHiddenFields: true,
      queryHiddenFields: false,
    });


    one sec, improving examplke

  • default discord avatar
    Lloyd
    7 months ago

    Does the

    sort

    remove the field that I need to exclude?

  • default discord avatar
    notchris
    7 months ago

    Oops

  • default discord avatar
    Lloyd
    7 months ago

    (Btw yes I am familiar with express, I've already written some custom endpoints for payload, but I thought there is other ways to exclude fields from a query without writing custom endpoints )

  • default discord avatar
    notchris
    7 months ago

    You know what, there's an easier way



    On your collection, you can define custom routes that extend the collection



    const Orders: CollectionConfig = {
        slug: 'orders',
        fields: [ /* ... */ ],
      endpoints: [
        {
          path: '/:id/tracking',
          method: 'get',
          handler: async (req, res, next) => {
            const tracking = await getTrackingInfo(req.params.id);
            if (tracking) {
              res.status(200).send({ tracking });
            } else {
              res.status(404).send({ error: 'not found' });
            }
          }
        }
      ],
    }


    So that's one way, not sure how to manage the fields in this instance



    Let me show a custom endpoint with express as an example

  • default discord avatar
    Lloyd
    7 months ago

    Wait a second, I think I got the custom field part figured out. Do you know by any chance how to exclude a field when doing a query with the Local API?



    This way I can save some bandwidth on the backend side, making the request faster

  • default discord avatar
    notchris
    7 months ago

    yes



    well possibly, id still get the collection, but then filter what i send

  • default discord avatar
    Lloyd
    7 months ago

    Oh I see, so there is no way to tell it to fetch only specific fields, it will get all of them

  • default discord avatar
    notchris
    7 months ago

    well



    one sec

  • default discord avatar
    Lloyd
    7 months ago

    In the docs I have this example


    // Result will be a paginated set of Posts.
    // See /docs/queries/pagination for more.
    const result = await payload.find({
      collection: "posts", // required
      depth: 2,
      page: 1,
      limit: 10,
      where: {}, // pass a `where` query here
      sort: "-title",
      locale: "en",
      fallbackLocale: false,
      user: dummyUser,
      overrideAccess: false,
      showHiddenFields: true,
      queryHiddenFields: false,
    });

    I saw no ways to fetch only specific fields

  • default discord avatar
    notchris
    7 months ago
    import express, { Request, Response } from "express";
    
    const app = express();
    
    // GET request route
    app.get("/getPosts", async (req: Request, res: Response) => {
        try {
        const posts = await payload.find({
          collection: "posts",
        }).map((post) => {
          const {id, foo} = post
    
          return {
            id,
            foo
          }
        })
        res.json(posts)
        } catch (err) {
            // handle error
          res.status(404).end()
        }
    });


    note: wrote this in discord so hopefully i didn't make a mistake



    so we use the local api on the server to get the collection, then we filter out the fields we dont want

  • default discord avatar
    Lloyd
    7 months ago

    It tells me that property

    map

    does not exist on type

    Promise

    , but I get how it works. I will make a custom route that will filter out the info before sending them to frontend. Thanks a lot ☺️

  • default discord avatar
    notchris
    7 months ago

    Ahhhh



    That's odd



    The promise should be resolved once you await it and return the right type



    Maybe i condensed it too much



    Let me know if you get it working 😄

  • default discord avatar
    Lloyd
    7 months ago

    I ended up doing this


      endpoints: [
        {
          // This returns the simulations that are supposed to be used to list them, so it contains only the title and the dates, it does not contain the actual content of the simulation like questions ecc
          path: '/custom/manual-simulations-list/:courseId/:courseTier',
          method: 'get',
          handler: async (req, res, next) => {
            const courseIdParam = req.params.courseId
            const courseTierParam = req.params.courseTier
    
            const queryForManualSimulationsList = {
              and: [
                {
                  'coursesWithAccess.course': {
                    equals: courseIdParam,
                  },
                },
                {
                  'coursesWithAccess.tier': {
                    equals: courseTierParam,
                  },
                },
              ],
            }
    
            const reqManualSimulationsList = await payload.find({
              collection: ManualSimulations.slug,
              where: queryForManualSimulationsList,
              depth: 0,
            })
    
            if (reqManualSimulationsList.docs.length > 0) {
              // The query did find at least one record
              const filteredManualSimulationsList =
                reqManualSimulationsList.docs.map((e) => {
                  delete e.simulationBlocks
                  return e
                })
    
              res.status(200).send(filteredManualSimulationsList)
              return
            }
            res.status(204).send('Request successful, but no content found.')
          },
        },
      ],

    It seems to be working.



    Btw if you're curious what I'm doing, I'm making a website for lessons people can take online, and then there will be some tests they can do. This endpoint basically sends to the frontend a list with the tests available (I only needed the titles and some basic info, sending all the questions contained in each test would have made a very big request with data that were not needed for the page that displays the tests, that's why I wanted to exclude it)

  • default discord avatar
    notchris
    7 months ago

    Ahhh I see



    Very cool!



    And I'm glad you got it working!!!

  • default discord avatar
    Lloyd
    7 months ago

    Thanks ☺️

  • default discord avatar
    notchris
    7 months ago

    @Lloyd Is there any benefit to using .json() opposed to .send() when sending found documents?

  • default discord avatar
    Lloyd
    7 months ago

    I'm not sure, I'm building the custom endpoints the same way I've seen on the Payload documentation. For example if you go there

    https://payloadcms.com/docs/rest-api/overview

    you will see them use

    send

    This is a code snippet from the page linked:


    import { CollectionConfig } from 'payload/types';
    
    // a collection of 'orders' with an additional route for tracking details, reachable at /api/orders/:id/tracking
    const Orders: CollectionConfig = {
        slug: 'orders',
        fields: [ /* ... */ ],
      endpoints: [
        {
          path: '/:id/tracking',
          method: 'get',
          handler: async (req, res, next) => {
            const tracking = await getTrackingInfo(req.params.id);
            if (tracking) {
              res.status(200).send({ tracking });
            } else {
              res.status(404).send({ error: 'not found' });
            }
          }
        }
      ],
    }

    They use

    send

    so I'm doing the same



    I just asked chatgpt and it told me that there are some benefits of using json(), do you mind if I send you the response in private? I don't know if it's allright to send so much stuff in a closed community-help question

  • default discord avatar
    notchris
    7 months ago

    Oh this is totally fine



    It will be helpful to people in the future



    But if you feel more comfortable sending a dm, please do so

  • default discord avatar
    Lloyd
    7 months ago

    Here's what chatgpt told me


    Yes, there are some benefits to using the .json() method over the .send() method when sending found documents in an HTTP response.
    
    The .json() method is specifically designed for sending JSON data in the response body, whereas the .send() method is a more generic method that can send various types of data, including strings, buffers, objects, and arrays. When using .json(), the data is automatically serialized to JSON format and the appropriate Content-Type header is set to application/json.
    
    Using .json() has a few advantages over using .send():
    
        More explicit: By using .json(), you are explicitly stating that the response contains JSON data, which makes the code more readable and easier to understand for other developers.
    
        Better performance: Since .json() automatically sets the Content-Type header to application/json, it can be more efficient than using .send(), which requires you to set the header manually. This can be especially important in large-scale applications where performance is critical.
    
        Consistency: By using .json(), you are ensuring that the response is always returned in JSON format, which can help ensure consistency in your API responses.
    
    In summary, using .json() when sending found documents is a good practice since it is more explicit, can improve performance, and promotes consistency in API responses.
  • default discord avatar
    Teun
    7 months ago

    I've built a tool/plugin that does this:

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


    It just filters out the fields that you want / don't want and you can use it on any payload rest endpoint

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.