2

Perhaps I haven't grasped GraphQL yet, but I want to update a complex record in the DB. I'm having trouble finding docs that explain this, which doesn't use a trivial example. I am using FaunaDB and trying to test things in the GraphQL playground before implementing it in JS.

Here is an an example of my schema:

type Event {
    name: String!
    email: String!
    eventName: String!
    location: String
    guests: [Guest!]!
    note: String!
    confirmed: Boolean!
    completed: Boolean!
    dateTimeStart: Time
    dateTimeEnd: Time
    sentConfirmation: Boolean!
}

type Guest @embedded {
    email: String!
    rsvp: Boolean
    results: SurveyAnswers
}

type SurveyAnswers @embedded {
    ~ 12 various fields
}

I want to update a field on the event: sentConfirmation, that's it.

I know the event Id and what I want to update the Boolean value to.

Is there a way to write a mutation to just pass in an ID and the updated Boolean value and change that single value, or do I need to literally pass in the entire object and all it's sub objects with just that one top level field updated?

StingyJack
  • 19,041
  • 10
  • 63
  • 122
Ivan
  • 1,093
  • 1
  • 10
  • 15
  • Have you tried it? The GraphQL spec is exactly what you say: you only need to pass in the key and fields whose values have changed. If you tried it and it didn't work, perhaps you could post more details about any errors you got. – David784 Jun 26 '20 at 21:43

3 Answers3

4

This should be possible by calling the autogenerated partialUpdate mutation. You might have not noticed it, since this is currently only available as a Schema Preview feature.

This means essentially that in order to enable it, you will need to add the following header to your HTTP Request:

X-Schema-Preview: partial-update-mutation

Note that since this Schema Preview feature adds a new autogenerated mutation, you will have to add this header when executing a GraphQL operation (i.e., sending request against the /graphql endpoint), and not when importing the schema (i.e., sending a request agains the /import endpoint). The reason is basically that all of the autogenerated queries and mutations are derived at runtime, and not when the schema is imported.

Now, if you have added the header as mentioned above, when calling some introspection query for reading the schema back (like the GraphQL Playground does for instance), you should notice there's a new mutation field and a new input object:

input PartialUpdateEventInput {
  name: String
  email: String
  eventName: String
  location: String
  guests: [PartialUpdateGuestInput!]
  note: String
  confirmed: Boolean
  completed: Boolean
  dateTimeStart: Time
  dateTimeEnd: Time
  sentConfirmation: Boolean
}

type Mutation {
  partialUpdateEvent(id: ID!, data: PartialUpdateEventInput!): Event
}

Since all of the fields are optional for this new input object, you should be able now to update just the sentConfirmation field:

mutation PartialUpdate {
  partialUpdateEvent(id: "269434161519919634", data: {
    sentConfirmation: true
  }) {
    _id
  }
}

All of the fields which are not included in the mutation will remain unaffected.

Establishing a parallel with FQL, this means that GraphQL's update mutation will act as FQL's Replace function, and GraphQL's partialUpdate mutation as FQL's Update function.

lregnier
  • 201
  • 2
  • 3
2

my first reflection was also: "have you tried it?" but I immediately assumed you did. Your problem is caused by the fact that all of your attributes are mandatory (as you specify with the exclamation (!) marks behind the attributes). These fields therefore are also checked on the update. Arguably, that might be unnecessary and I'll ask my colleagues whether we might not want to change that.

You can in each case 'solve' your problem by removing the required fields. In that case you can perfectly do:

# Write your query or mutation here
mutation test {
  createEvent(data: {
    name: "Test",
    email: "test@test.com",
    eventName: "lala"
    note: "I am a note",
    confirmed:true,
    completed:true,
    sentConfirmation: false
  })
  {_id}

}

followed by:

# Write your query or mutation here
mutation test {
  updateEvent(id: "269430330059915782", data:{
    completed:false
  })
  {_id}
}

and your document's boolean 'completed' field will be updated.

enter image description here

Brecht De Rooms
  • 1,802
  • 7
  • 15
  • 1
    "*I'll ask my colleagues whether we might not want to change that*" - you might want to disclose that (if) you are a faunadb employee. – Bergi Jun 26 '20 at 23:00
  • 1
    See Leo's response. I am indeed a FaunaDB dev advocate and contacted Leo to help you further since there indeed appear to be plans and there is already a solution foreseen. – Brecht De Rooms Jun 27 '20 at 10:24
  • Thanks for the response @BrechtDeRooms! I'm still learning GraphQL and NoSQL, but I assumed that setting required fields would only affect create and not update, since I thought I only updated what I needed, thus the confusion of why it kept asking for more fields to be filled in when trying to run the update query. At least to me, the current functionality seems unintuitive, thus this question was asked for something that seemed like it should just work dead simply, but it looks like you are working on fixing that already with the preview, which worked for me by the way! Thanks again! – Ivan Jun 29 '20 at 15:50
0

Usually, I create two different input types. In your case it would be createEventInput and updateEventInput.

In the createEventInput all or most fields are mandatory while in the updateEventInput you could have more fields optional. But you need to be careful not to end up updating you record with empty values for fields that are actually mandatory for your internal business logic.

createEvent(data: createEventInput) { ... }

updateEvent(data: updateEventInput) { ... }

FaunaDB creates similar input types for both create* and update* mutations. But you can create you own input types as stated in the FaunaDB docs

user2814599
  • 1,060
  • 1
  • 13
  • 27