Let's imagine I have a createPost mutation that inserts a new post. In a typical app, that mutation can either:
Succeed, returning a Post.
Fail, throwing an error (I use apollo-errors to handle this).
What I'd like to implement is a middle scenario, where the mutation succeeds (returning a Post); but also somehow returns a warning to the user (e.g. Your post is similar to post XYZ or similar).
What would be a good GraphQL pattern to implement this? Adding a warning field to the Post type seems a little weird, but then again I'm not sure how to return both a Post and a Warning in the same mutation? Any ideas?
(Note that I'm using this scenario as an example, I'm interested in the general pattern of returning extra post-mutation data, not finding similar posts specifically)
All my mutations return a wrapping payload type rather than a single value type (e.g. Post in your case), I also don't ever throw in GraphQL unless it's a real system error -- if it's the consequence of user input or is an otherwise expected case, I model it into the return type.
Returning a wrapping payload is generally considered a best practice because a) your mutation should return entry points for everything in the graph that may have changed (not just the new post), and b) it gives you the easy ability to add new fields to the return type at a later time.
Remember, a mutation is essentially a function that takes in some input data and the current graph, and returns a new graph. It's generally a mistake to think in terms of REST-like CRUD operations.
type CreatePostError = {
// Whatever you want
}
type CreatePostSuccess = {
post: Post!
warning: String
}
union CreatePostPayload = CreatePostSuccess | CreatePostError
mutation {
// Other mutations
createPost(/* args /*): CreatePostPayload
}
Related
I'm using react-apollo#2.5.6
I have a component, when you click on it, it will based on "select" state and issue either an add or a remove operation.
Currently I'm doing this to have 2 mutations function injected to my component. Is that the correct way to do it? Am I able to just use one Mutation ( HOC ) instead of multiple ?
<Mutation mutation={ADD_STUFF}>
{(addStuff) => (
<Mutation mutation={REMOVE_STUFF}>
{(removeStuff) => {
And later in the wrapped component, I will do something like that
onClick={(e) => {
e.preventDefault()
const input = {
variables: {
userId: user.id,
stuffId: stuff.id,
},
}
// Based on selected state, I will call either add or remove
if (isSelected) {
removeStuff(input)
} else {
addStuff(input)
}
}}
Thanks
Everything is possible but usually costs time and money ;) ... in this case simplicity, readability, manageablility.
1st solution
Common mutation, f.e. named 'change' with changeType parameter.
Of course that requires API change - you need a new resolver.
2nd solution
Using graphql-tag you can construct any query from the string. Take an inspiration from this answer - with 'classic graphql HOC' pattern.
This solution doesn't require API change.
I think using two different Mutation components does not make sense. If I understand correctly, there can be two ways to solve your problem.
Using Apollo client client.mutate function to do manual mutation based on the state and set mutation and variables properties based on the new state. To access the client in current component, you need to pass along the client from parent component where it was created to child components where mutation is taking place.
Using single Mutation component inside render method of your component and setting mutation and variables attributes based on the state variable.
The approach that you are using is working as you said, but to me looks like you are delegating some logic to the UI that should be handled by the underlying service based on the isSelected input.
I think that you should create a single mutation for ADD_STUFF and REMOVE_STUFF, I would create the ADD_OR_REMOVE_STUFF mutation, and choose the add or remove behavior on the resolver.
Having one mutation is easier to maintain/expand/understand, if the logic requires something else besides add/remove, for example if you have to choose add/remove/update/verify/transform, would you nest 5 mutations?
In the previous case the single mutation could be named MULTI_HANDLE_STUFF, and only have that one mutation called from the UI.
I am trying to do something effectively like this
`query GetAllUsers($fields: [String]) {
users {
...$fields
}
}`
Where my client (currently Apollo for react) then passes in an array of fields in the variables section. The goal is to be able to pass in an array for what fields I want back, and that be interpolated to the appropriate graphql query. This currently returns a GraphQL Syntax error at $fields (expects a { but sees $ ). Is this even possible? Am I approaching this the wrong way?
One other option I had considered was invoking a JavaScript function and passing that result to query(), where the function would do something like the following:
buildQuery(fields) {
return gql`
query {
users {
${fields}
}
}`
}
This however feels like an unecessary workaround.
Comments summary:
Non standard requirements requires workarounds ;)
You can use fragments (for predefined fieldsets) but they probably won't be freely granular (field level).
Variables are definitely not for query definition (but for variables used in query).
Daniel's suggestion: gql-query-builder
It seams that graphQL community is great and full of people working on all possible use cases ... it's enough to search for solutions or ask on SO ;)
Can the below be achieved with graph ql:
we have getusers() / getusers(id=3) / getusers(name='John). Can we use same query to accept different parameters (arguments)?
I assume you mean something like:
type Query {
getusers: [User]!
getusers(id: ID!): User
getusers(name: String!): User
}
IMHO the first thing to do is try. You should get an error saying that Query.getusers can only be defined once, which would answer your question right away.
Here's the actual spec saying that such a thing is not valid: http://facebook.github.io/graphql/June2018/#example-5e409
Quote:
Each named operation definition must be unique within a document when
referred to by its name.
Solution
From what I've seen, the most GraphQL'y way to create such an API is to define a filter input type, something like this:
input UserFilter {
ids: [ID]
names: [String]
}
and then:
type Query {
users(filter: UserFilter)
}
The resolver would check what filters were passed (if any) and query the data accordingly.
This is very simple and yet really powerful as it allows the client to query for an arbitrary number of users using an arbitrary filter. As a back-end developer you may add more options to UserFilter later on, including some pagination options and other cool things, while keeping the old API intact. And, of course, it is up to you how flexible you want this API to be.
But why is it like that?
Warning! I am assuming some things here and there, and might be wrong.
GraphQL is only a logical API layer, which is supposed to be server-agnostic. However, I believe that the original implementation was in JavaScript (citation needed). If you then consider the technical aspects of implementing a GraphQL API in JS, you might get an idea about why it is the way it is.
Each query points to a resolver function. In JS resolvers are simple functions stored inside plain objects at paths specified by the query/mutation/subscription name. As you may know, JS objects can't have more than one path with the same name. This means that you could only define a single resolver for a given query name, thus all three getusers would map to the same function Query.getusers(obj, args, ctx, info) anyway.
So even if GraphQL allowed for fields with the same name, the resolver would have to explicitly check for whatever arguments were passed, i.e. if (args.id) { ... } else if (args.name) { ... }, etc., thus partially defeating the point of having separate endpoints. On the other hand, there is an overall better (particularly from the client's perspective) way to define such an API, as demonstrated above.
Final note
GraphQL is conceptually different from REST, so it doesn't make sense to think in terms of three endpoints (/users, /users/:id and /users/:name), which is what I guess you were doing. A paradigm shift is required in order to unveil the full potential of the language.
a request of the type works:
Query {
first:getusers(),
second:getusers(id=3)
third:getusers(name='John)
}
Currently in GraphQL JS implementation there is no standard way of returning a descriptive success message from mutation, such as "User created successfully", etc. My question has 2 parts:
My thinking is that it is beneficial to return a success message from the API. That way the messages can be uniform across different clients (e.g. web and mobile). Clients don't have to implement their custom messages for each API call. But I'm not sure what the best practice is. Are there any drawbacks of returning a descriptive success message from mutation calls (besides increased response size)?
If I decide to do it, is there a standard way in graphql-js to do this?
For example, is it possible to get back something like:
{
data: {
mutationName: {var1:"val1"}
},
messages: {
mutationName:"User created successfully"
}
}
Reasons not to do this
There is now a considerable amount of client code that depends on the shape of returned results being as they are. You would need to fork those if you were to implement your idea, at the cost of considerable effort, in order to benefit from your change.
Reason to stick with language spec as it is today
Meanwhile, what you want to achieve is (if I have understood you right) entirely achievable within the current spec.
Effective solution to stated problem
Why not do this (we'll make the mutation be user-creation):
schema {
mutation: Mutation
query: Query
}
type Mutation {
newUser(params: NewUserInput): NewUserResult
}
input NewUserInput {
# ...
}
type NewUserResult {
successMessage(languageCode: LanguageCode): String
newUser: User
}
# need to define Query, NewUserInput, User
This allows you to return a success message if that's what your system requires, without needing to change the spec of the GraphQL language.
If you really must...
(Source: graphql errors and status messages in graphql-js)
At least in express-graphql, there is a way to pass in extensions to it: https://github.com/graphql/express-graphql/blob/master/src/index.js#L89
Pardon the naive question, but I've looked all over for the answer and all I've found is either vague or makes no sense to me. Take this example from the GraphQL spec:
query getZuckProfile($devicePicSize: Int) {
user(id: 4) {
id
name
profilePic(size: $devicePicSize)
}
}
What is the point of naming this query getZuckProfile? I've seen something about GraphQL documents containing multiple operations. Does naming queries affect the returned data somehow? I'd test this out myself, but I don't have a server and dataset I can easily play with to experiment. But it would be good if something in some document somewhere could clarify this--thus far all of the examples are super simple single queries, or are queries that are named but that don't explain why they are (other than "here's a cool thing you can do.") What benefits do I get from naming queries that I don't have when I send a single, anonymous query per request?
Also, regarding mutations, I see in the spec:
mutation setName {
setName(name: "Zuck") {
newName
}
}
In this case, you're specifying setName twice. Why? I get that one of these is the field name of the mutation and is needed to match it to the back-end schema, but why not:
mutation {
setName(name: "Zuck") {
...
What benefit do I get specifying the same name twice? I get that the first is likely arbitrary, but why isn't it noise? I have to be missing something obvious, but nothing I've found thus far has cleared it up for me.
The query name doesn't have any meaning on the server whatsoever. It's only used for clients to identify the responses (since you can send multiple queries/mutations in a single request).
In fact, you can send just an anonymous query object if that's the only thing in the GraphQL request (and doesn't have any parameters):
{
user(id: 4) {
id
name
profilePic(size: 200)
}
}
This only works for a query, not mutation.
EDIT:
As #orta notes below, the name could also be used by the server to identify a persistent query. However, this is not part of the GraphQL spec, it's just a custom implementation on top.
We use named queries so that they can be monitored consistently, and so that we can do persistent storage of a query. The duplication is there for query variables to fill the gaps.
As an example:
query getArtwork($id: String!) {
artwork(id: $id) {
title
}
}
You can run it against the Artsy GraphQL API here
The advantage is that the same query each time, not a different string because the query variables are the bit that differs. This means you can build tools on top of those queries because you can treat them as immutable.