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
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 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 """
I am using the new version Keystone Next and I am trying to connect multiple items at once using an array of ids. It seems connect supports that, accepting an array of objects.
const FINISH_VOCABULARY_QUIZ_MUTATION = gql`
mutation FINISH_VOCABULARY_QUIZ_MUTATION(
$userId: ID!
$wordId: ID!
) {
updateUser(id: $userId, data: {
wrongAnswers: {
connect: [{id: "idblabla"}, {id: "idblabla2"}]
}
}) {
id
}
}`;
But what I just can't seem to figure out is how do I pass this array of ids as a variable to my mutation.
I understand that I would need to create a new type? The documentation is still unfinished, so there is nothing on that yet.
I have also tried using string interpolation to form my query, but it seems that it's not a thing in GraphQl.
This is more of a GraphQL question than a KeystoneJS but one but to head to the right direction here you'd need to change your query to something like below:
const FINISH_VOCABULARY_QUIZ_MUTATION = gql`
mutation FINISH_VOCABULARY_QUIZ_MUTATION(
$userId: ID!,
$ids: [UserWhereUniqueInput!]!
) {
updateUser(id: $userId, data: {
wrongAnswers: {
connect: $ids
}
}) {
id
}
}`;
And then map your array of ids to an array of objects with id fields.
There is a better method:
const FINISH_VOCABULARY_QUIZ_MUTATION = gql`
mutation FINISH_VOCABULARY_QUIZ_MUTATION(
$userId: ID!,
$data: SomeAPIDefinedMutationUniqueInput
) {
updateUser(id: $userId, data: $data)
id
}
}`;
This way you:
don't have to define types for internal arguments ($wordsIdWrong: [WordWhereUniqueInput]);
can reuse/share this mutation - import it from some, common for queries, place (dir) - just call it with different data variables;
easier for reading/maintenance;
PS. To be honest, there should be some specific [to quizes] mutation (don't use userUpdate for that), with user (or better quiz) id defined within SomeAPIDefinedUniqueInput.
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 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
}
}
}
`;