how to match queries with apollo's refetchQuery - graphql

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

Related

How to move fields list fragment interpolation outside of the brackets

Here is a mock of the current graphQL I'm working with.
const aDynamicListOfFieldsComingFromElsewhere = 'foo bar anotherField etc'
const query = gql`{
QueryResult: TableName {
Data {
id
name
${aDynamicListOfFieldsComingFromElsewhere}
}
}
}`
This... functionally speaking, works. But is regarded as a bad approach for multiple reasons, one of which is the linting support provided by eslint-plugin-graphql.
Eslint gives me a hint by giving me the following error:
Invalid interpolation - fragment interpolation must occur outside of the brackets graphql/template-strings
I managed to find some good examples with variables, but none that provided a way to include an externally defined variable.
Thanks in advance for your help!
You can use directives for this. Specification for GraphQL: https://graphql.org/learn/queries/#directives
Query should looks like this:
const query = gql`
query QueryResult($withFoo: Boolean!, $withBar: Boolean!, $withAnotherField: Boolean!, $withEtc: Boolean!) {
Data {
id
name
foo #include(if: $withFoo)
bar #include(if: $withBar)
anotherField #include(if: $withAnotherField)
etc #include(if: $withEtc)
}
}
`
And also the variables that should be passed into your GraphQL client:
{
withFoo: true,
withBar: true,
withAnotherField: false,
withEtc: true
}

Building Mutation Graphql Query

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

How to conditionally apply a GraphQL filter based on the value of a passed in argument?

Update: for my particular use case, #langpavel's solution will work. However, it avoids answering the original question... is it possible to conditionally apply a GraphQL filter based on the value of a passed in argument?
I am passing in an argument into my GraphQL query called $env that I would like to use in conjunction with a draft boolean in the markdown frontmatter of my blog posts in order to filter out drafts during production.
My query, in an abbreviated form, is below. As you can see, I have a filter function that is filtering based on whether the blog post is a draft or not. What I can't figure out how to do is take advantage of the $env argument to only apply the filter when $env is equal to production.
export const pageQuery = graphql`
query BlogPosts($env: String!) {
markdownRemark(
filter: { frontmatter: { draft: { eq: true } } }
) {
frontmatter {
title
date(formatString: "MMMM DD, YYYY")
draft
}
}
}
`
In case of gatsby, you should probably write two queries and select one conditionally based on process.env.NODE_ENV:
export const pageQuery = (process.env.NODE_ENV === 'production') ?
graphql`
query BlogPosts {
markdownRemark(
filter: { frontmatter: { draft: { ne: true } } }
) { frontmatter { ... } }
}
` : graphql`
query BlogPosts {
markdownRemark { frontmatter { ... } }
}
`
The GraphQL query you provided is invalid because the $env variable is not used in any fields. I think for your purpose, $env would be better passed outside of the GraphQL query. If you're serving GraphQL over HTTP, you could send it as a query parameter or an HTTP header. Then you could pass the value to your GraphQL resolvers using the context concept that most GraphQL libraries provide.

Filter Apollo connection with nested variables

I'd like to specify a nested argument as the filter for my #connection directive. Is it possible to do something like this:
input CommentsInput {
postId: String!
cursors: CommentsCursorInput
}
query getComments($input: CommentInput) {
getComments(input: $input)
#connection(key: "comments", filter: ["input.postId"]) {
comments {
id
commentBody
createdDate
liked
likeCount
}
}
}
postId is passed in input. This is a contrived example and I know I can structure this differently, but my main questions is can I use nested arguments as a filter, or can I only use top-level arguments in that filter array? I didn't see any discussion of this in the docs.

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