I am trying to use graphql to query a store's orders in a bulk operation. I am using a tags variable in the graphql request to query orders with specific tags. I have been following Shopify's documentation in order to do this.
Here is what I've been trying:
const variables = {
"tags": "tag:foo AND tag:bar"
};
const query = gql`
mutation getOrders($tags: String!) {
bulkOperationRunQuery(
query:"""
query {
orders(query: $tags) {
edges {
node {
id
email
}
}
}
}
"""
) {
bulkOperation {
id
url
status
}
userErrors {
field
message
}
}
}
`;
const bulkOperation = await graphQLClient.request(query, variables);
However, I always get this error back from the api:
Variable $tags is declared by getOrders but not used
Does anyone know if there is a way to use a graphql variable in that bulk operation? Thanks!
""" is multiline string based on docs, which would means its just a string and no vars are allowed. You can use js variable substitution like ${tags} inside templated string and bypass this all together or try if concatenation works like """ 1st part """" + $tags + """ 2nd part """
Related
I am trying to create a graphql query where the query data type and filter parameters will be passed dynamically based on user input.
I have written the below query which filters using only one field shipdate.
const GET_SHIPDATA_WITH_FILTER = gql`
query GetShipData($shipdateStart: timestamptz, $shipdateEnd: timestamptz, $limit: Int) {
shipdata(where: {shipdate: { _gte: $shipdateStart, _lte: $shipdateEnd}},limit: $limit) {
status
import_time
shipdate
}
}
`;
const variables = {
shipdateStart: "some date",
shipdateEnd: "some date",
limit: 50,
};
If no filter is passed I'm using this one
const GET_SHIPDATA = gql`
query GetShipData($limit: Int) {
shipdata(limit: $limit) {
status
import_time
shipdate
}
}
`;
const variables = {
limit: 50,
};
You can see I have written two queries to handle two types of filters which won't work if I want to add more filters.
Now I am trying to write a single dynamic query where if the user wants to add more filters like status: {_eq: $status} or import_time: { _gt: $importTimeStart, _lt: $importTimeEnd} then I will pass the variables and the query will dynamically handle the filters. Something like
const GET_SHIPDATA = gql`
query GetShipData($allfilters: AllFilterTypes) {
shipdata(filter: $allfilters) {
status
import_time
shipdate
}
}
`;
const variables = {
//pass allFilters based on user input,
};
Btw I'm using react and hasura if it helps anyway.
Hasura already exposes types in your GraphQL schema that refer to "filter conditions". In Hasura, they're called Bool_Exp (short for boolean expression) and they map directly to the where clause.
If you just update your query to receive a shipdata_bool_exp you'll be able to build up a dynamic filter expression in your application code and it will work as expected.
const GET_SHIPDATA_WITH_FILTER = gql`
query GetShipData($filter: shipdata_bool_exp!) {
shipdata(where: $filter,limit: $limit) {
status
import_time
shipdate
}
}
`;
I'd like to be able to dynamically choose which query variables I use in GraphQL.
For example, it seems a little redundant to need three separate queries:
const getAllStops = gql`
query trafficStops {
trafficStops {
id
date
}
}
`
const getStopsAfter = gql`
query trafficStops($after: String!) {
trafficStops(after: $after) {
id
date
}
}
`
const getStopsBefore = gql`
query trafficStops($before: String!) {
trafficStops(before: $before) {
id
date
}
}
`
Is there a way in which I could pass not just the variables before or after but whether I'd like to use one, the other, neither, or both into a single query instead of having multiple queries?
Yes, you just have to make your arguments optional. The exclamation mark at String! requires the argument to be a string and not null. Hence, by removing it you could write your single query as
const getAllStops = gql`
query trafficStops($after: String, $before: String) {
trafficStops(after: $after, before: $before) {
id
date
}
}
`
I'm using graphql-tag so i'm going to use that syntax.
Lets say I have this query:
const query = gql`
query user(id: String) {
user(id: $id) {
id
}
}
`
Whats the best patten to reuse that same query document node if on a different call I want the fields username and email in addition to id without having to rewrite the entire query again like:
const query = gql`
query user(id: String) {
user(id: $id) {
id
username
email
}
}
`
I'm using react-apollo on the frontend if that makes things anymore interesting.
Edit:
Just to clarify... something like this
const userIdFrag = gql`
fragment UserId on User {
id
}
`
const fullUserFrag = gql`
fragment FullUser on User {
id
username
email
}
`
const generateQuery = (documentNode) => {
return gql`
query user(id: String) {
user(id: $id) {
...documentNode
}
}
${documentNode}
`
}
const idQuery = generateQuery(userIdFrag);
const fullUserQuery = generateQuery(fullUserFrag);
(The above does work but give me errors from graphql in the console, which leads me to believe this is not something I should be doing)
Based on your comment the following should work:
const generateQuery = (documentNode, fragment) => {
return gql`
query user(id: String) {
user(id: $id) {
...${fragment}
}
}
${documentNode}
`
}
const idQuery = generateQuery(userIdFrag, 'UserId');
const fullUserQuery = generateQuery(fullUserFrag, 'FullUser');
Basically the fragment name used is the actual one that needs to be spread while the whole documentNode object is put at the end, after query's closing bracket
I am not the very expert on the topic, but here is what I have been able to find out. (if you see any mistakes in my assumptions, let me know).
I found this article that makes some good points against dynamically generating gql queries/mutations. It seems like you get some nice benefits with the static approach, although it's a bit more typing.
But, in case you do need to have dynamic fields, I haven't been able to find anything bad about using the #skip directive GraphQL provides. Here the docs ref.
For the case of using it in react-apollo they also have it in their docs.
So, your code can end up looking something like this:
const query = gql`
query user($id: String, $skipUserMeta: Boolean!) {
user(id: $id) {
id
username #skip(if: $skipUserMeta)
email #skip(if: $skipUserMeta)
}
}
`
You just pass the skipUserMeta as a variable alongside the id field.
NOTE: I actually found a video which talks about the exact same approach here
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.
I have a graphQl query where I use a Prismic uid to get a specific user. Now I also want to get a user space from another prismic set of data called prismicSpaces but instead of looking for the data based on the uid on prismicSpaces I want to pass it an identifier from prismicUsers data object called location_id. Seen as location_id is not at the root like uid how can I use that to pass it down to prismicSpaces ?
below is my current query which works great but I need a way to pass on the location_id like the second example below this.
ps. I'm using gatsby and prismic
export const query = graphql`
query UsersQuery($uid: String!) {
page: prismicUsers(uid: { eq: $uid }) {
uid
type
data {
location_id
}
}
}
spaces: prismicSpaces {
uid
data {
name
}
}
}
`;
Example but this does not work.
export const query = graphql`
query UsersQuery($uid: String!, $space_id:String! ) {
page: prismicUsers(uid: { eq: $uid }) {
uid
type
data {
space_id
}
}
}
spaces: prismicSpaces(uid: {eq: { $space_id }) {
uid
data {
name
}
}
}
`;