简体   繁体   中英

Which value should be returned by a apollo graphql mutation?

I'm using apollo graphql to call some mutation to change db data. I need some advice which value should be returned usually.

For insertOne() I would return the ID of the new document and for removing a dataset I would also return the ID, to remove the dataset on the client cache.

But what about updateOne - as it could be a very simple task as shown below, but sometimes it can do some complex update? I'm not quite sure which would be the most useful returning value.

In this simple example I'm just updating some content of a specific mongodb document. Right now the WriteResult of this method is returned. But is this really useful?

I see the follwing options:

  1. Returning writeResult object - which has the need to be defined in the backend
  2. Check writeResult and return boolean, eg result.nModified > 0
  3. Return the content result - which would be at the end just the input content
  4. Returning the updated document - which would need a findOne({ _id: id })
  5. Just returning true , which maybe is not very useful?!

I know there is not a single solution for all use cases, but maybe someone could explain which way to go in general...

server

async updateContent(id, name, value) {
  const Content = this.db.collection('content')
  return Content.updateOne(
    { _id: id },
    { $set: { [name]: value } }
  )
}

client

updateContent({
  variables: { id, name, value }
})
  .then((response) => {
    // response.data.updateContent
  })
  .catch((error) => console.error(error))

graphql

mutation updateContent($id: ID!, $name: String, $value: String) {
  updateContent(id: $id, name: $name, value: $value) {
    nModified
  }
}

schema

type WriteResult {
  nModified: Int,
  nRemoved: Int,
  nInserted: Int,
  n: Int
  ok: Int
}

type Mutation {
  updateContent(id: ID!, name: String, value: String): ContentString
}

extending earlier comment...

usually mutation resolver returns... mutated type (but it's a graphql, you don't have to query for all fields/props; response will be filtered out; returning data = success)... even deleted ones, surprise: :D

Returning mutated object type can be important for more advanced FE techniques like optimistic response . This helps you avoid refetching entire item list when you're updating one element .

in this case

Types are not defined properly - this is your source of concerns

Probably you should define it more like (input types are more manageable):

type ContentType {
  id: ID!
  content: String!
  author: User!
  createdAt: Date!
  updatedAt: Date!
}
type ContentCreateInputType {
  name: String!
  value: String!
}     
type ContentUpdateInputType {
  id: ID!
  name: String!
  value: String!
}

type Stats {
  nModified: Int,
  nRemoved: Int,
  nInserted: Int,
  n: Int
  ok: Int
}    
type ContentUpdatePayload {
  content: ContentType
  stats: Stats
}    

type Mutation {
  updateContent(input: ContentUpdateInputType!): ContentUpdatePayload
}    

This is a bit similiar idea to popular relay style cursor pagination .

Your mutation can be:

mutation updateContent($input: ContentUpdateInputType) {
  updateContent(input: $input) {
    stats {
      nModified
    }
  }
}

... in this case your resolver must return only stats object, no findOne required... but in practice it should return at least:

{ 
  content: {
    id
  }
  stats: {
    nModified
    nRemoved
    // etc.
  }
}

... because your mutation can be also:

mutation updateContent($input: ContentUpdateInputType) {
  updateContent(input: $input) {
    content {
      id
      content
      author {
        id
        name
      }
    }
    stats {
      nModified
    }
  }
}

Returning { content: { id } stats: {....} } (js object, the minimal mutation response) is neccessary (very important) to resolve entire query . Server resolver will know it has not complete ContentType object (mutation ContentUpdatePayload element) and it will call ContentType resolver (with known/already resolved id as arg) to populate it (of course it will call Author type resolver, too).

As you can notice/see above, returning only a content { id } (from this mutation, part of ContentType ) can be a part of the optimization - no overfetching - because id can be taken from mutation args and the entire content is resolved/read from DB/service only when required.

conclusion

Returning not typed responses simply doesn't allow this kind of 'GraphQL magic'.

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