Building Mutation Graphql Query - graphql

I am quite new to GraphQL so I am struggling a little bit to understand how to write a proper Query on the front-end.
So, this is the Mutation I've on the server-side
type Mutation {
addTerminal(terminal: TerminalInput): Terminal
// other stuff not related
}
type Terminal {
terminalId: String!
merchantId: String
terminalDesignator: String
}
input TerminalInput {
terminalId: String!
merchantId: String
terminalDesignator: String
}
I believe it is using the right structure, but when I try to connect with the client-side im a bit confused.
This is the query I've on the front-end.
export const ADD_TERMINAL_MUTATION = () => (
mutation addTerminalMutation($terminalId: TerminalInput) {
addTerminal(terminal: { terminalId: $terminalId }) {
terminalId,
merchantId,
terminalDesignator,
}
}
);
and when I fire it to the server, I receive the following feedback:
Variable "$terminalId" of type "TerminalInput" used in position expecting type "String!".
So I changed to this:
addTerminal(terminal: { terminalId: "123" }) {
and got the error
Variable "$terminalId" is never used in operation "addTerminalMutation".
If I change to
mutation addTerminalMutation($terminalId: String!) {
It says that the TerminalId wasnt provided, but if I log it, it can be seen
So, what is the right way to write this ?
Thanks.

You need to change addTerminalMutation($terminalId: TerminalInput) to addTerminalMutation($terminalId: String!) to indicate the correct type.

First, the
Variable "$terminalId" is never used in operation "addTerminalMutation".
is caused by passing the variable in line
mutation addTerminalMutation($terminalId: TerminalInput)
and never referencing it later.
The problem in the second part seems that the terminalId parameter is not decomposed.
The parameter in the line
addTerminal(terminal: { terminalId: $terminalId })
is expected to be a String!.
You could do something like this:
addTerminal(input: $terminalId)
to pass in the whole terminal id object.
See this for further info:
https://blog.apollographql.com/designing-graphql-mutations-e09de826ed97

Related

Can Apollo read partial fragments from cache?

I have a simple mutation editPerson. It changes the name and/or description of a person specified by an id.
I use this little snippet to call the mutator from React components:
function useEditPerson(variables) {
const gqlClient = useGQLClient();
const personFragment = gql`fragment useEditPerson__person on Person {
id
name
description
}`;
return useMutation(gql`
${personFragment}
mutation editPerson($id: ID!, $description: String, $name: String) {
editPerson(id: $id, description: $description, name: $name) {
...useEditPerson__person
}
}
`, {
variables,
optimisticResponse: vars => {
const person = gqlClient.readFragment({
id: vars.id,
fragment: personFragment,
});
return {
editPerson: {
__typename: "Person",
description: "",
name: "",
...person,
...vars,
},
};
},
});
}
This works well enough unless either the name or description for the indicated person hasn't yet been queried and does not exist in the cache; in this case person is null. This is expected from readFragment - any incomplete fragment does this.
The thing is I really need that data to avoid invariant errors - if they're not in the cache I'm totally okay using empty strings as default values, those values aren't displayed anywhere in the UI anyway.
Is there any way to read partial fragments from the cache? Is there a better way to get that data for the optimistic response?
I guess you use the snippet in the form that has all the data you need. So, you can pass the needed data to your useEditPerson hook through the arguments and then use in optimistic response, and then you won't need to use gqlClient.

Omitting the field in response with GraphQL Apollo

I am using Apollo GraphQL server and directives.
Here is my simple schema. Notice the directive on the token field, User type.
const typeDefs = `
directive #allow(service: String) on FIELD_DEFINITION
type User {
email: String!
pass: String!
... other fields here
token: String #allow(service: "login")
}
type Mutation {
login(email: String!, pass: String!): User
}`;
I would like to return the token field only if the login has been called. Otherwise, I would like to return the User object without the token field, all I could find is throwing an Exception or returning the null in the "token" field.
class SkipDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field, details) {
const { resolve = defaultFieldResolver } = field;
field.resolve = async function (...args) {
// If called in context different from "login"
// Here I would like to just "delete" the "token" field
else {
const result = await resolve.apply(this, args);
return result;
}
};
}
}
Ideas?
If a field is requested, it should be returned with either a value matching the field's type or else null. To do otherwise would break the spec.
There is no way you can modify this behavior through a schema directive. A field definition directive can only change runtime behavior by modifying the field's resolver. However, by the time the resolver is called, the selection set has already been determined so it's too late to modify it. Returning null or throwing an error are pretty much the only two options.
You might be able to implement some kind of workaround through either the formatResponse option or a custom plugin. However, because this behavior would break the spec, there's no telling if it wouldn't cause issues with client libraries or other tools.

Document all potential errors on GraphQL server?

For a mutation addVoucher there are a limited list of potential errors that can occur.
Voucher code invalid
Voucher has expired
Voucher has already been redeemed
At the moment I'm throwing a custom error when one of these occurs.
// On the server:
const addVoucherResolver = () => {
if(checkIfInvalid) {
throw new Error('Voucher code invalid')
}
return {
// data
}
}
Then on the client I search the message description so I can alert the user. However this feels brittle and also the GraphQL API doesn't automatically document the potential errors. Is there a way to define the potential errors in the GraphQL schema?
Currently my schema looks like this:
type Mutation {
addVoucherResolver(id: ID!): Order
}
type Order {
cost: Int!
}
It would be nice to be able to do something like this:
type Mutation {
addVoucherResolver(id: ID!): Order || VoucherError
}
type Order {
cost: Int!
}
enum ErrorType {
INVALID
EXPIRED
REDEEMED
}
type VoucherError {
status: ErrorType!
}
Then anyone consuming the API would know all the potential errors. This feels like a standard requirement to me but from reading up there doesn't seem to be a standardises GraphQL approach.
It's possible to use a Union or Interface to do what you're trying to accomplish:
type Mutation {
addVoucher(id: ID!): AddVoucherPayload
}
union AddVoucherPayload = Order | VoucherError
You're right that there isn't a standardized way to handle user-visible errors. With certain implementations, like apollo-server, it is possible to expose additional properties on the errors returned in the response, as described here. This does make parsing the errors easier, but is still not ideal.
A "Payload" pattern has emerged fairly recently for handling these errors as part of the schema. You see can see it in public API's like Shopify's. Instead of a Union like in the example above, we just utilize an Object Type:
type Mutation {
addVoucher(id: ID!): AddVoucherPayload
otherMutation: OtherMutationPayload
}
type AddVoucherPayload {
order: Order
errors: [Error!]!
}
type OtherMutationPayload {
something: Something
errors: [Error!]!
}
type Error {
message: String!
code: ErrorCode! # or a String if you like
}
enum ErrorCode {
INVALID_VOUCHER
EXPIRED_VOUCHER
REDEEMED_VOUCHER
# etc
}
Some implementations add a status or success field as well, although I find that making the actual data field (order is our example) nullable and then returning null when the mutation fails is also sufficient. We can even take this one step further and add an interface to help ensure consistency across our payload types:
interface Payload {
errors: [Error!]!
}
Of course, if you want to be more granular and distinguish between different types of errors to better document which mutation can return what set of errors, you won't be able to use an interface.
I've had success with this sort of approach, as it not only documents possible errors, but also makes it easier for clients to deal with them. It also means that any other errors that are returned with a response should serve as an immediately red flag that something has gone wrong with either the client or the server. YMMV.
You can use scalar type present in graphql
just write scalar JSON and return any JSON type where you want to return it.
`
scalar JSON
type Response {
status: Boolean
message: String
data: [JSON]
}
`
Here is Mutation which return Response
`
type Mutation {
addVoucherResolver(id: ID!): Response
}
`
You can return from resolver
return {
status: false,
message: 'Voucher code invalid(or any error based on condition)',
data: null
}
or
return {
status: true,
message: 'Order fetch successfully.',
data: [{
object of order
}]
}
on Front end you can use status key to identify response is fetch or error occurs.

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);
}
});

GraphQL mutation variables

I'm trying to do a simple mutation using GraphQL with the GraphiQL interface. My mutation looks like this:
mutation M($name: String) {
addGroup(name:$name) {
id,
name
}
}
with variables:
{
"name": "ben"
}
But it gives me the error: Variable $name of type "String" used in position expecting type "String!"
If I change my mutation to mutation M($name: String = "default") it works as expected. This looks like it's related to the type system, but I can't seem to figure out what the problem is.
You probably defined the input name as a non-null string (something like type: new GraphQLNonNull(GraphQLString) if using js server, or String! in plain GraphQL).
So your input in the mutation must match, which means it must also be a non-null string. If you change to the following, it should work:
mutation M($name: String!) {
addGroup(name:$name) {
id,
name
}
}
Also if you define a default value as you did, it will be a non-null string.
Finally, you could drop the requirement of being a non-null in the server.
I think in you addGroup() mutation the args for name is of type String! that is new GraphQLNonNull(GraphQLString) but in your mutation you specify as String which conflicts with the type system.
When you have an error like this check your database model, in my case I checked my schema and as mongo pluralizes the words was causing me error but I could fix it, also check the documentation.
mutation {
createProject(
name:"project two",
description:"project two"
) {
name
}
}
=> works

Resources