Graphql combine multiple identical mutations - graphql

I'm using a type GraphQL service that I have no control over. They have separate mutations which create/update/delete a single entity with something like
mutation DeleteThing($personId: ID!, $input: DeleteInput!) {
deleteThing(personId: $personId, input: $input) {
...ClientFragment
}
}
variables: { personId:'SomeId', input: { id: 'ThingId'}}
Now I may need to change 10-40 of this type of entity per request and I want to return only the final ClientFragment after every query has ran. What is the best way to go about this?
I know I can do something like:
mutation DeleteThing($personId: ID!, $input1: DeleteInput!, $input2:DeleteInput!) {
d1:deleteThing(personId: $personId, input: $input1) {
...ClientFragment
}
d2:deleteThing(personId: $personId, input: $input2) {
...ClientFragment
}
d3:...
}
variables: { personId:'SomeId', input1: { id: 'ThingId'}, input2: { id: 'OtherThingId'}}
But I may not care if one of these mutations throws a 404, since I just want them gone. I also would have to query after the mutation to get the final ClientFragment as these mutations may run asynchronously. Perhaps there is a more elegant solution?

Related

How to write mutation in graphql-playgraound for the output

I want to see output in graphql-playground of mutations which I don't know how to do. Code is below.
type Query {
""" Gets single referral program"""
program:Program
setting(level_id:Int!): Setting
}
type Mutation {
"""Define loyalty program for a club"""
define_program(input:DefineProgram): Program
"""Club level loyalty program configuration"""
config_program(input:ProgramConfig): Setting
get_individual_code(input:Individual): String
get_group_code(input:Group): String
}
type Program {
id: String
name: String
referral_level_id:Int
enabled:Boolean
}
input DefineProgram {
name: String!
type:ReferralType!
referral_level_id:Int!
enabled:Boolean = true
userid: Int
}
input ProgramConfig {
program_id: Int!
level_id:Int!
type:CodeType!
expirydays:String!
validity:Int!
limit:Int!
userid:Int
}
input Individual {
member_id:Int!
days_to_expire:Int!
first_name:String!
last_name:String!
email:String!
userid:Int
}
input Group {
member_id: Int!
days_to_expire:Int!
people:Int!
userid:Int
}
input Filter {
program_id: Int!
level_id: Int!
}
type Setting {
program_id: Int
program_name:String
level_id:Int
tier:String
type:CodeType
expirydays:String
validity:Int
limit:Int
}
enum CodeType {
NUMERIC
ALPHA_NUMERIC
}
enum ReferralType {
INDIVIDUAL,
GROUP,
BOTH
}
Can anyone tell me how to write for all mutations to get data in graphql-playground.
Help if anyone can.
GraphQL playground will actually give you autocomplete capabilities. It sounds like you're just starting out with consuming GraphQL, though, so I would recommend you start with the learning queries from graphql.org, and then once you're comfortable there, you should be able to put together queries / mutations for your specific examples.

FaunaDB - How to bulk update list of entries within single graphQL mutation?

I want to bulk update list of entries with graphQL mutation in faunaDB.
The input data is list of coronavirus cases from external source. It will be updated frequently. The mutation should update existing entries if the entry name is present in collectio and create new ones if not present.
Current GRAPHQL MUTATION
mutation UpdateList($data: ListInput!) {
updateList(id: "260351229231628818", data: $data) {
title
cities {
data {
name
infected
}
}
}
}
GRAPHQL VARIABLES
{
"data": {
"title": "COVID-19",
"cities": {
"create": [
{
"id": 22,
"name": "Warsaw",
"location": {
"create": {
"lat": 52.229832,
"lng": 21.011689
}
},
"deaths": 0,
"cured": 0,
"infected": 37,
"type": "ACTIVE",
"created_timestamp": 1583671445,
"last_modified_timestamp": 1584389018
}
]
}
}
}
SCHEMA
type cityEntry {
id: Int!
name: String!
deaths: Int!
cured: Int!
infected: Int!
type: String!
created_timestamp: Int!
last_modified_timestamp: Int!
location: LatLng!
list: List
}
type LatLng {
lat: Float!
lng: Float!
}
type List {
title: String!
cities: [cityEntry] #relation
}
type Query {
items: [cityEntry!]
allCities: [cityEntry!]
cityEntriesByDeathFlag(deaths: Int!): [cityEntry!]
cityEntriesByCuredFlag(cured: Int!): [cityEntry!]
allLists: [List!]
}
Everytime the mutation runs it creates new duplicates.
What is the best way to update the list within single mutation?
my apologies for the delay, I wasn't sure exactly what the missing information was hence why I commented first :).
The Schema
An example of a part of a schema that has arguments:
type Mutation {
register(email: String!, password: String!): Account! #resolver
login(email: String!, password: String!): String! #resolver
}
When such a schema is imported in FaunaDB there will be placeholder functions provided.
The UDF parameters
As you can see all the function does is Abort with the message that the function still has to be implemented. The implementation starts with a Lambda that takes arguments and those arguments have to match what you defined in the resolver.
Query(Lambda(['email', 'password'],
... function body ...
))
Using the arguments is done with Var, that means Var('email') or Var('password') in this case. For example, in my specific case we would use the email that was passed in to get an account by email and use the password to pass on to the Login function which will return a secret (the reason I do the select here is that the return value for a GraphQL resolver has to be a valid GraphQL result (e.g. plain JSON
Query(Lambda(['email', 'password'],
Select(
['secret'],
Login(Match(Index('accountsByEmail'), Var('email')), {
password: Var('password')
})
)
))
Calling the UDF resolver via GraphQL
Finally, how to pass parameters when calling it? That should be clear from the GraphQL playground as it will provide you with the docs and autocompletion. For example, this is what the auto-generated GraphQL docs tell me after my schema import:
Which means we can call it as follows:
mutation CallLogin {
login (
email: "<some email>"
password: "<some pword>"
)
}
Bulk updates
For bulk updates, you can also pass a list of values to the User Defined Function (UDF). Let's say we would want to group a number of accounts together in a specific team via the UI and therefore want to update multiple accounts at the same time.
The mutation in our Schema could look as follows (ID's in GraphQL are similar to Strings)
type Mutation { updateAccounts(accountRefs: [ID]): [ID]! #resolver }
We could then call the mutation by providing in the id's that we receive from FaunaDB (the string, not the Ref in case you are mixing FQL and GraphQL, if you only use GraphQL, don't worry about it).
mutation {
updateAccounts(accountRefs: ["265317328423485952", "265317336075993600"] )
}
Just like before, we will have to fill in the User Defined Function that was generated by FaunaDB. A skeleton function that just takes in the array and returns it would look like:
Query(Lambda(['arr'],
Var('arr')
))
Some people might have seen an easier syntax and would be tempted to use this:
Query(Lambda(arr => arr))
However, this currently does not work with GraphQL when passing in arrays, it's a known issue that will be fixed.
The next step is to actually loop over the array. FQL is not declarative and draws inspiration from functional languages which means you would do that just by using a 'map' or a 'foreach'
Query(Lambda(["accountArray"],
Map(Var("accountArray"),
Lambda("account", Var("account")))
))
We now loop over the list but don't do anything with it yet since we just return the account in the map's body. We will now update the account and just set a value 'teamName' on there. For that we need the Update function which takes a FaunaDB Reference. GraphQL sends us strings and not references so we need to transform these ID strings to a reference with Ref as follows:
Ref(Collection('Account'), Var("account"))
If we put it all together we can add an extra attribute to a list of accounts ids as follows:
Query(Lambda(["accountArray"],
Map(Var("accountArray"),
Lambda("account",
Do(
Update(
Ref(Collection('Account'), Var("account")),
{ data: { teamName: "Awesome live-coders" } }
),
Var("account")
)
)
)
))
At the end of the Map, we just return the ID of the account again with Var("account") in order to return something that is just plain JSON, else we would be returning FaunaDB Refs which are more than just JSON and will not be accepted by the GraphQL call.
Passing in more complex types.
Sometimes you want to pass in more complex types. Let's say we have a simple todo schema.
type Todo {
title: String!
completed: Boolean!
}
And we want to set the completed value of a list of todos with specific titles to true. We can see in the extended schema generated by FaunaDB that there is a TodoInput.
If you see that extended schema you might think, "Hey that's exactly what I need!" but you can't access it when you write your mutations since you do not have that part of the schema at creation time and therefore can't just write:
type Mutation { updateTodos(todos: [TodoInput]): Boolean! #resolver }
As it will return the following error.
However, we can just add it to the schema ourselves. Fauna will just accept that you already wrote it and not override it (make sure that you keep the required fields, else your generated 'createTodo' mutation won't work anymore).
type Todo {
title: String!
completed: Boolean!
}
input TodoInput {
title: String!
completed: Boolean!
}
type Mutation { updateTodos(todos: [TodoInput]): Boolean! #resolver }
Which means that I can now write:
mutation {
updateTodos(todos: [{title: "test", completed: true}])
}
and dive into the FQL function to do things with this input.
Or if you want to include the ID along with data you can define a new type.
input TodoUpdateInput {
id: ID!
title: String!
completed: Boolean!
}
type Mutation { updateTodos(todos: [TodoUpdateInput]): Boolean! #resolver }
Once you get the hang of it and want to learn more about FQL (that's a whole different topic) we are currently writing a series of articles along with code for which the first one appeared here: https://css-tricks.com/rethinking-twitter-as-a-serverless-app/ which is probably a good gentle introduction.

How get rid of redundant wrapper object of a mutation result?

When I'm making a request to my backend through a mutation like that:
mutation{
resetPasswordByToken(token:"my-token"){
id
}
}
I'm getting a response in such format:
{
"data": {
"resetPasswordByToken": {
"id": 3
}
}
}
And that wrapper object named the same as the mutation seems somewhat awkward (and at least redundant) to me. Is there a way to get rid of that wrapper to make the returning result a bit cleaner?
This is how I define the mutation now:
export const ResetPasswordByTokenMutation = {
type: UserType,
description: 'Sets a new password and sends an informing email with the password generated',
args: {
token: { type: new GraphQLNonNull(GraphQLString) },
captcha: { type: GraphQLString },
},
resolve: async (root, args, request) => {
const ip = getRequestIp(request);
const user = await Auth.resetPasswordByToken(ip, args);
return user.toJSON();
}
};
In a word: No.
resetPasswordByToken is not a "wrapper object", but simply a field you've defined in your schema that resolves to an object (in this case, a UserType). While it's common to request just one field on your mutation type at a time, it's possible to request any number of fields:
mutation {
resetPasswordByToken(token:"my-token"){
id
}
someOtherMutation {
# some fields here
}
andYetAnotherMutation {
# some other fields here
}
}
If we were to flatten the structure of the response like you suggest, we would not be able to distinguish between the data returned by one mutation from another. We likewise need to nest all of this inside data to keep our actual data separate from any returned errors (which appear in a separate errors entry).

how to match queries with apollo's refetchQuery

My fundimental question is do the variables for queries need to be exact for refetchQueries to work. Or can you give it a subset of variables and it will match similar queries.
Consider the following ....
<Query<NotesQuery, NotesQueryVariables>
query={notesQuery}
variables={{
input: {
notebookId: notebookContext.id,
first: 20
}
}}
>
</Query>
and the following mutation:
client
.mutate<NoteCreateOrUpdateMutation, NoteCreateOrUpdateMutationVariables>({
mutation: noteCreateOrUpdateMutation,
variables: {
input: {
noteId: note ? note.id : undefined,
subjectIds: noteSubjects,
notebookId: notebookContext.id,
authorId: userContext.id,
content: noteContent,
context: noteCaption,
}
},
refetchQueries: [
{
query: notesQuery,
variables: { input: { notebookId: notebookContext.id } } as NotesQueryVariables
}
]
})
when I do that mutation it is NOT refetching the note query with the pagination
If I add the first: 20 parameter -- it works.
I would like it to clear all noteQueries that match with the given parameters. Is that possible?
I believe you'll be wanting to add #connection directives to your gql definitions of notesQuery and measurementsQuery. You didn't post those, so unfortunately I can't show you exactly what that would look like for your use case.
Anyway, the #connection directive will allow Apollo to match on notebookId for example, while ignoring the value of first.
Unfortunately, you've bundled all your input into the object input, and I don't know how you would select just notebookId with the filter. Assuming that your gql definition looks something like this for notesQuery:
const notesQuery = gql`
query notes($input: InputType!) {
notes(input: $input) #connection(key: "notes", filter: ["input['notebookId']"]) {
id
...
}
}
`;
^^^ Unfortunately, that won't work because of the way that apollo-utilities/lib/storeUtils.js -> getStoreKeyName() function works. It'll just ignore the above attempt to get better resolution than an arg name, i.e. can't go beyond input. Any string in the filter array that doesn't match an arg name is silently ignored.
Looks like you'll have to modify your schema.
More info at: https://www.apollographql.com/docs/react/features/pagination.html#connection-directive

How do I create a mutation that pushes to an array rather than replacing it?

I've been playing with GraphQL recently, and am currently learning about mutations. I'm a bit confused with something. I have a model Post with relation Comments. I have a mutation that looks like this:
mutation addCommentToPost {
updatePost(
id: "POST-1",
comments: [{
body: "Hello!"
}]
) {
id,
comments {
id,
body
}
}
}
The problem is, whenever I run this, it seems to remove all the comments and sets the comments to only the one I just added. To be more specific, how do I write a mutation that pushes to the comments array rather than replacing it?
You are using a mutation called updatePosts, which I assume (based on the name) simply updates a post by replacing the fields that are passed. If you want to use the updatePosts mutation to add a comment, you will first have to query for the post to get the current list of comments, add your comment to the end, and then call updateComment with the entire list of comments (including the one that you just added to the end).
However, this isn't really a good solution, especially if the list of comments is potentially very long. If you have the ability to change the GraphQL server, you should create a new mutation on the server with a signature like addComment(postId: ID, comment: CommentInput). In the resolve function for that mutation, simply add the comment that is passed to the end of the list of current comments.
// resolver for addComment:
addComment(root, args) {
// validate inputs here ...
const post = db.getPost(args.postId);
post.comments.append(args.comment);
db.writePost(post.id, post);
}
db.getPost and db.writePost are functions you have to define yourself to retrieve/write a post from/to wherever you store it.
It's important to note that unlike a SQL or Mongo query, a GraphQL mutation itself doesn't have any meaning without the resolve functions. What the mutation does is defined entirely inside its resolve function. Mutation names and arguments only gain meaning together with the resolve function. It's up to you (or the GraphQL server developers in your company) to write the resolve functions.
The way this situation is currently solved in the Graphcool API is to use a create mutation for the Comment that links to the Post. This is called a nested connect mutation.
This is how it would look like:
mutation {
createComment(
text: "Hello!"
postId: "POST-1"
) {
id
text
post {
comments {
id
}
}
}
}
In the future, other nested arguments like comments_set or comments_push could be introduced, then pushing would be possible like this:
mutation addCommentToPost {
updatePost(
id: "POST-1",
comments_push: [{
body: "Hello!"
}]
) {
id,
comments {
id,
body
}
}
}
Disclosure: I work at Graphcool.
You can use those code as an example for mutation.
module.exports = (refs) => ({
type: refs.commentType,
args: {
id: {
type: GraphQLString
},
body: {
type: GraphQLString
}
},
resolve: (parent, args, root) => {
return createUser(args);
}
});

Resources