Vincit / objection-graphql

GraphQL schema generator for objection.js
MIT License
307 stars 34 forks source link

Relations not loading after mutation #82

Open ryanvanderpol opened 5 years ago

ryanvanderpol commented 5 years ago

I have a mutation defined as follows


const mutation = {
    description: 'Updates a Person',
    type: graphQlSchema.getType('Persons'),
    args: {
        id: {
            type: new GraphQLNonNull(GraphQLInt),
            description: 'Person ID',
        },
        first_name: {
            type: new GraphQLNonNull(GraphQLString),
            description: 'First Name',
        },
        last_name: {
            type: new GraphQLNonNull(GraphQLString),
            description: 'Last Name',
        }
    },
    resolve: async (root, userData) => {
        const { id, first_name, last_name } = userData;
        return await Person.query().findById(id).patch({ first_name, last_name }).returning('*');
    },
};

Which I call from my app like

mutation($id: Int!, firstName: String!, lastName: String!) {
    updatePerson(id: $id, first_name: $firstName, last_name: $lastName) {
        id,
        first_name,
        last_name,
        pets {
                name,
                toys {
                        brand_name,
                        quantity
                }
        }
    }
}

The mutation part works fine, but when the data is returned from the server, it's missing the pets object and everything below it. Interestingly (maybe), pets actually comes back as null rather than being omitted completely, so it seems like maybe it tried to complete the relation, but nothing came back.

Any idea what could be going on here?

DaKaZ commented 4 years ago

@ryanvanderpol I think what is wrong is that you need to do two things. First, separate out your find and patch query, secondly add an EAGER to your patch to pull in the pets, and third use the patchAndFetch instead of just patch. Here is an untested example but it is similar to what is working for me:

resolve: async (root, userData) => {
    const { id, first_name, last_name } = userData;
    const updating = await Person.query().findById(id);
    return await updating.$query().eager('pets').patchAndFetch({ first_name, last_name });
}
ryanvanderpol commented 4 years ago

Thanks, @DaKaZ. in my OP I noted that a pet also has toys. Would I need to add toys to the eager list? And if that's the case, what happens when I have way, way more complex objects that are nested way deeper than this? I need to explicitly add everything to the eager query?

I guess my question is, why can't I get a mutation to work like the built-in Objection.js types? Using the built-in .model types the query can dynamically load the return object and its nested types based on the GraphQL request -- I just want my mutations to return objects using the same behavior.

tsimons commented 4 years ago

This might not be ideal depending on your project, but you can refetch the query after performing the mutation. Apollo boost has this an option when making a query.

If performance is a concern, and you don't want to eager load all of the relations, you'll have to traverse the ast and find the relations being requested. You can see an example how to do that in the SchemaBuilder class: https://github.com/Vincit/objection-graphql/blob/master/lib/SchemaBuilder.js#L301