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:
findOne({ _id: id })
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 .
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.
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.