1
votes

I'm working on a GraphQL AppSync project for 6 months by now and I'm already quite familiar with the concept so far.

But I came across one thing, which is not explained in tutorials or documentations at all. What is the best practice of the return type of a Mutation? (especially of a partial update)

Here is one simplified sample schema:

type Article {
    uuid: ID
    title: String
    description: String
    price: Int
    tax: Int
    category_uuid: ID
    status: Int
    type: Int
}

input ArticleUpdateInput {
    uuid: ID!
    title: String
    description: String
    price: Int
    tax: Int
    category_uuid: ID
    status: Int
    type: Int
}

type Mutation {
    updateArticle(input: ArticleUpdateInput!): Article!
}

The following mutation would be valid:

mutation foo {
    updateArticle(input: {
        uuid: "c63c6dcb-6c09-4952-aae2-26e3fde47262",
        title: "BBQ Burger",
        price: 699
    }) {
        __typename
        uuid
        title
        description
        price
        tax
        category_uuid
        status
        type
    }
}

Since I only specified the title and the price the other fields of the response will be null like so:

{
    "data": {
        "updateArticle": {
            "__typename": "Article",
            "uuid": "c63c6dcb-6c09-4952-aae2-26e3fde47262",
            "title": "BBQ Burger",
            "description": null,
            "price": 699,
            "tax": null,
            "category_uuid": null
            "status": null
            "type": null
        }
    }
}

What would be a best practice here to avoid to return these null fields? Should I trigger a getArticle query after the update and return the whole article record from the database? I think this would be very inefficient because if you want to add n articles, there will be 2*n roundtrips to the database.

Any ideas so far?

1
What are you trying to do exactly? Do you need the additional fields (description, status...) to be filled with data or you just don't need them in the response at all? What data source are you using? - Tinou
@Tinou I'm using an amazon rds (mysql) instance which is connected to the AppSync service via lambda. My question is, what should I do with the remaining fields in the response, if I'm mutating only a few of them? Would it be a good solution to just return the uuid for example? Is it really best practice to provide all of the fields of type in the response? If so, I have to get the remaining fields from the database or am I wrong? - widdy
It's really up to your application requirements, if your front-end only needs the uuid back then the output of your mutation can be just a String. If your application requires the entire Article, then you would need to do a read after your update to grab all the fields. - Tinou

1 Answers

4
votes

If you are returning an Article type from a mutation, it should have the same values as if you were to subsequently return it from a different query.

Think of a mutation as a function that "mutates" the GraphQL from one state to another, and then (conventionally) returns an entry point to all parts of the GraphQL that may have change.

I can see in the comment replies to your question that you're worried about performance. My advice is to not let performance be justification for modelling your schema poorly, pretty much every performance issue i've seen with GraphQL does have solutions, so focus on the modelling.

Additionally, you probably don't want to return the Article directly, it limits your ability to include other changes. Say a User type has an publishedArticleCount denormalised field, clients would need to know when this has changed, which means it needs to be accessible via the mutation. So you might want to do something like this:

type UpdateArticlePayload {
    article: Article!
    author: User!
}

type Mutation {
    updateArticle(input: ArticleUpdateInput!): UpdateArticlePayload!
}

This payload pattern makes it easier to change the scope of your mutation over time, whilst your original modelling ties you into a relatively narrow use case.