简体   繁体   中英

Nested Mongo Calls In Node/Express

I'm trying to build a REST api which is being consumed by a separate React frontend. On one page I need to draw data from separate collections in a single mongo database, and bundle it together before returning the response to the frontend, so as to minimise api calls. I have the following collections in my database:

  • Users
  • Items

And the data structure for each of these is as follows:

  • UsersCollection = [{id: ObjectId, completedItems=[ObjectId, ObjectId, ...], ...}]
  • ItemsCollection = [{id: ObjectId, name: string, ...}]

In my node/express application I would like to do a call to get all users, for each user, query the items collection to pull the name of each item before returning the response.

When I eventually return the response I want to be able to return an object of the following structure, but without altering the structure of how I'm storing the data.

response = [{id: ObjectId, completedItems=[{id: ObjectId, name: string}, {id: ObjectId, name: string}]}]

The issue that I am running into is that when I make this call, when there are no items in the completedItems array for the user, the entire backend crashes when it trys to do the second api call, and I end up with error messages such as "Cannot read property 'name' of null".

I'm presuming I am missing some kind of error handling here, or perhaps I am not thinking about the overall structure in the correct way.

As this is my first major REST api, I don't know whether I should be trying to merge this information on the backend before I return the response, or whether I should handle this on the front end, return a response for the users call to the front end and then make a second api call to the backend to get the items data.

Any guidance would be much appreciated!

'''lang-js

const getUsers = async (req, res, next) => { 
    let users 
    try { 
        users = await User.find({}, {name:1, items: 1})
    } catch (err) { 
        return next(new Error('Could not fetch inductions').code(500)) 
    } 
    for (let i in users){
        let {id, name, items} = users[i] 
        item = await Item.findById(item, {name:1}) 
    } 
    users[i] = { 
        id: id, name: name, item: {
            name: item.name, id: item.id
        } 
    } 
}

'''

I would propose 3 options:

  1. To use "populate" in Mongoose, you can edit your mongoose user model to allow completedItems to reference ItemsCollection, and then when querying users and using "populate" method, mongoose will automatically retrieve the needed items for you.

    You may do something like:

     const userSchema = Schema({ _id: Schema.Types.ObjectId, completedItems: [{ type: Schema.Types.ObjectId, ref: 'Items' }] });

    Notice that Items here refers to the name you give to ItemsSchema.

    Check the documentation for more details

  2. To check for completedItems array length first before trying to query ItemsCollection, like that if the user has no completedItems you won't have to query ItemsCollection.

    Doing something like:

     if(user.completedItems.length.= 0) ItemsCollection:find({id. user.completedItems})
  3. The third option is to use GeaphQL for this and like that you will be able to retrieve all the required data to do something like this:

     UsersCollection.find().populate({ path: 'ItemsList', model: 'Items' })

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM