GraphQL - Execute sub query conditionally - graphql

I'm trying to optimise the query executed by some of my react components that are shared across the whole app, such as Footer and Header components.
I'm trying not to fetch the Student Solution details when the variable institutionPath is not provided.
query organisationAndInstitution($organisationName: String!, $institutionPath: String!, $fetchInstitution: Boolean!){
organisation(where: {
name: $organisationName
}){
name
}
studentSolutionRelationships(where:{
AND: [
{
status: PUBLISHED
},
{
studentSolution: {
status: PUBLISHED
}
}
]
}) #include(if: $fetchInstitution) {
status
}
}
To do so, I added a fetchInstitution boolean variable and added the #include(if: $fetchInstitution) directive.
But directives seem to apply only on fields, not on whole queries. So I wonder if what I want to do is possible, because the way I wrote it is invalid.

Any field in a GraphQL document can be explicitly included using the #include directive or explicitly excluded using the #skip directive. The directive should be provided after the field name and arguments, but before the field's selection set, if it has one, as shown in your question:
studentSolutionRelationships(where:{
#...input fields omitted for brevity
}) #include(if: $fetchInstitution) {
status
}
The directive takes a single argument (if) which must be a Boolean value. This value may be a literal (i.e. true or false) or a variable of the Boolean type. GraphQL does not provide a way to evaluate expressions -- any conditional logic has to be reside in the client code and be used to determine the value of the variable passed to the if argument.
The directives may be applied to any field in a document, including root-level ones like studentSolutionRelationships and organisation in the question. In fact, you can exclude all root fields using these directives -- just keep in mind that in such a scenario, the query will still run and just return an empty object.
In other words, your approach here is correct. If the query is failing, it's because of an unrelated issue.

Related

Is it possible to add a layer of grouping under query in graphql? [duplicate]

All docs and tutorials usually show simple examples of mutations that look like this:
extend type Mutation {
edit(postId: String): String
}
But this way the edit method has to be unique across all entities, which to me seems like not a very robust way to write things. I would like to describe mutation similar to how we describe Queries, something like this:
type PostMutation {
edit(postId: String): String
}
extend type Mutation {
post: PostMutation
}
This seems to be a valid schema (it compiles and I can see it reflected in the generated graph-i-ql docs). But I can't find a way to make resolvers work with this schema.
Is this a supported case for GraphQL?
It's possible but generally not a good idea because:
It breaks the spec. From section 6.3.1:
Because the resolution of fields other than top‐level mutation fields must always be side effect‐free and idempotent, the execution order must not affect the result, and hence the server has the freedom to execute the field entries in whatever order it deems optimal.
In other words, only fields on the mutation root type should have side effects like CRUD operations.
Having the mutations at the root makes sense conceptually. Whatever action you're doing (liking a post, verifying an email, submitting an order, etc.) doesn't rely on GraphQL having to resolve additional fields before the action is taken. This is unlike when you're actually querying data. For example, to get comments on a post, we may have to resolve a user field, then a posts field and then finally the comments field for each post. At each "level", the field's contents are dependent on the value the parent field resolved to. This normally is not the case with mutations.
Under the hood, mutations are resolved sequentially. This is contrary to normal field resolution which happens in parallel. That means, for example, the firstName and lastName of a User type are resolved at the same time. However, if your operation type is mutation, the root fields will all be resolved one at a time. So in a query like this:
mutation SomeOperationName {
createUser
editUser
deleteUser
}
Each mutation will happen one at a time, in the order that they appear in the document. However, this only works for the root and only when the operation is a mutation, so these three fields will resolve in parallel:
mutation SomeOperationName {
user {
create
edit
delete
}
}
If you still want to do it, despite the above, this is how you do it when using makeExecutableSchema, which is what Apollo uses under the hood:
const resolvers = {
Mutation: {
post: () => ({}), // return an empty object,
},
PostMutation: {
edit: () => editPost(),
},
// Other types here
}
Your schema defined PostMutation as an object type, so GraphQL is expecting that field to return an object. If you omit the resolver for post, it will return null, which means none of the resolvers for the returning type (PostMutation) will be fired. That also means, we can also write:
mutation {
post
}
which does nothing but is still a valid query. Which is yet another reason to avoid this sort of schema structure.
Absolutely disagree with Daniel!
This is an amazing approach which helps to frontenders fastly understand what operations have one or another resource/model. And do not list loooong lists of mutations.
Calling multiple mutations in one request is common antipattern. For such cases better to create one complex mutation.
But even if you need to do such operation with several mutations you may use aliases:
await graphql({
schema,
source: `
mutation {
op1: article { like(id: 1) }
op2: article { like(id: 2) }
op3: article { unlike(id: 3) }
op4: article { like(id: 4) }
}
`,
});
expect(serialResults).toEqual([
'like 1 executed with timeout 100ms',
'like 2 executed with timeout 100ms',
'unlike 3 executed with timeout 5ms',
'like 4 executed with timeout 100ms',
]);
See the following test case: https://github.com/nodkz/conf-talks/blob/master/articles/graphql/schema-design/tests/mutations-test.js
Methods like/unlike are async with timeouts and works sequentially

GraphQL: Mutation inside Query Object [duplicate]

All docs and tutorials usually show simple examples of mutations that look like this:
extend type Mutation {
edit(postId: String): String
}
But this way the edit method has to be unique across all entities, which to me seems like not a very robust way to write things. I would like to describe mutation similar to how we describe Queries, something like this:
type PostMutation {
edit(postId: String): String
}
extend type Mutation {
post: PostMutation
}
This seems to be a valid schema (it compiles and I can see it reflected in the generated graph-i-ql docs). But I can't find a way to make resolvers work with this schema.
Is this a supported case for GraphQL?
It's possible but generally not a good idea because:
It breaks the spec. From section 6.3.1:
Because the resolution of fields other than top‐level mutation fields must always be side effect‐free and idempotent, the execution order must not affect the result, and hence the server has the freedom to execute the field entries in whatever order it deems optimal.
In other words, only fields on the mutation root type should have side effects like CRUD operations.
Having the mutations at the root makes sense conceptually. Whatever action you're doing (liking a post, verifying an email, submitting an order, etc.) doesn't rely on GraphQL having to resolve additional fields before the action is taken. This is unlike when you're actually querying data. For example, to get comments on a post, we may have to resolve a user field, then a posts field and then finally the comments field for each post. At each "level", the field's contents are dependent on the value the parent field resolved to. This normally is not the case with mutations.
Under the hood, mutations are resolved sequentially. This is contrary to normal field resolution which happens in parallel. That means, for example, the firstName and lastName of a User type are resolved at the same time. However, if your operation type is mutation, the root fields will all be resolved one at a time. So in a query like this:
mutation SomeOperationName {
createUser
editUser
deleteUser
}
Each mutation will happen one at a time, in the order that they appear in the document. However, this only works for the root and only when the operation is a mutation, so these three fields will resolve in parallel:
mutation SomeOperationName {
user {
create
edit
delete
}
}
If you still want to do it, despite the above, this is how you do it when using makeExecutableSchema, which is what Apollo uses under the hood:
const resolvers = {
Mutation: {
post: () => ({}), // return an empty object,
},
PostMutation: {
edit: () => editPost(),
},
// Other types here
}
Your schema defined PostMutation as an object type, so GraphQL is expecting that field to return an object. If you omit the resolver for post, it will return null, which means none of the resolvers for the returning type (PostMutation) will be fired. That also means, we can also write:
mutation {
post
}
which does nothing but is still a valid query. Which is yet another reason to avoid this sort of schema structure.
Absolutely disagree with Daniel!
This is an amazing approach which helps to frontenders fastly understand what operations have one or another resource/model. And do not list loooong lists of mutations.
Calling multiple mutations in one request is common antipattern. For such cases better to create one complex mutation.
But even if you need to do such operation with several mutations you may use aliases:
await graphql({
schema,
source: `
mutation {
op1: article { like(id: 1) }
op2: article { like(id: 2) }
op3: article { unlike(id: 3) }
op4: article { like(id: 4) }
}
`,
});
expect(serialResults).toEqual([
'like 1 executed with timeout 100ms',
'like 2 executed with timeout 100ms',
'unlike 3 executed with timeout 5ms',
'like 4 executed with timeout 100ms',
]);
See the following test case: https://github.com/nodkz/conf-talks/blob/master/articles/graphql/schema-design/tests/mutations-test.js
Methods like/unlike are async with timeouts and works sequentially

Are mutation methods required to be on the top level?

All docs and tutorials usually show simple examples of mutations that look like this:
extend type Mutation {
edit(postId: String): String
}
But this way the edit method has to be unique across all entities, which to me seems like not a very robust way to write things. I would like to describe mutation similar to how we describe Queries, something like this:
type PostMutation {
edit(postId: String): String
}
extend type Mutation {
post: PostMutation
}
This seems to be a valid schema (it compiles and I can see it reflected in the generated graph-i-ql docs). But I can't find a way to make resolvers work with this schema.
Is this a supported case for GraphQL?
It's possible but generally not a good idea because:
It breaks the spec. From section 6.3.1:
Because the resolution of fields other than top‐level mutation fields must always be side effect‐free and idempotent, the execution order must not affect the result, and hence the server has the freedom to execute the field entries in whatever order it deems optimal.
In other words, only fields on the mutation root type should have side effects like CRUD operations.
Having the mutations at the root makes sense conceptually. Whatever action you're doing (liking a post, verifying an email, submitting an order, etc.) doesn't rely on GraphQL having to resolve additional fields before the action is taken. This is unlike when you're actually querying data. For example, to get comments on a post, we may have to resolve a user field, then a posts field and then finally the comments field for each post. At each "level", the field's contents are dependent on the value the parent field resolved to. This normally is not the case with mutations.
Under the hood, mutations are resolved sequentially. This is contrary to normal field resolution which happens in parallel. That means, for example, the firstName and lastName of a User type are resolved at the same time. However, if your operation type is mutation, the root fields will all be resolved one at a time. So in a query like this:
mutation SomeOperationName {
createUser
editUser
deleteUser
}
Each mutation will happen one at a time, in the order that they appear in the document. However, this only works for the root and only when the operation is a mutation, so these three fields will resolve in parallel:
mutation SomeOperationName {
user {
create
edit
delete
}
}
If you still want to do it, despite the above, this is how you do it when using makeExecutableSchema, which is what Apollo uses under the hood:
const resolvers = {
Mutation: {
post: () => ({}), // return an empty object,
},
PostMutation: {
edit: () => editPost(),
},
// Other types here
}
Your schema defined PostMutation as an object type, so GraphQL is expecting that field to return an object. If you omit the resolver for post, it will return null, which means none of the resolvers for the returning type (PostMutation) will be fired. That also means, we can also write:
mutation {
post
}
which does nothing but is still a valid query. Which is yet another reason to avoid this sort of schema structure.
Absolutely disagree with Daniel!
This is an amazing approach which helps to frontenders fastly understand what operations have one or another resource/model. And do not list loooong lists of mutations.
Calling multiple mutations in one request is common antipattern. For such cases better to create one complex mutation.
But even if you need to do such operation with several mutations you may use aliases:
await graphql({
schema,
source: `
mutation {
op1: article { like(id: 1) }
op2: article { like(id: 2) }
op3: article { unlike(id: 3) }
op4: article { like(id: 4) }
}
`,
});
expect(serialResults).toEqual([
'like 1 executed with timeout 100ms',
'like 2 executed with timeout 100ms',
'unlike 3 executed with timeout 5ms',
'like 4 executed with timeout 100ms',
]);
See the following test case: https://github.com/nodkz/conf-talks/blob/master/articles/graphql/schema-design/tests/mutations-test.js
Methods like/unlike are async with timeouts and works sequentially

Can you spread a GraphQL field?

Basically I'm trying to spread a GraphQL field so I don't have as much nesting.
For instance:
query {
user(id: aasdfasdf) {
...address {
street
}
}
}
output
{
user: {
street: '111 Cool Street'
}
}
No. From the spec:
When querying an Object, the resulting mapping of fields are conceptually ordered in the same order in which they were encountered during query execution, excluding fragments for which the type does not apply and fields or fragments that are skipped via #skip or #include directives.
The structure of the response will always match the structure of the request. GraphQL does not inherently support any syntax to manipulate the structure of the returned response. In other words, you get what you request, and you can only request what is in the schema.
Although it's experimental, if you're using a javascript client, you could take a look at graphql-lodash. It plugs into existing clients and enables you to use custom directives to apply arbitrary transformations to your response.

Error: BabelPluginRemoveGraphQL: String interpolations

I am getting below error when trying to pass variable dynamically in gatsBy Graphql.
Error
Module build failed (from ./node_modules/gatsby/dist/utils/babel-loader.js):
Error: BabelPluginRemoveGraphQL: String interpolations are not allowed in graphql fragments. Included fragments should be referenced as `...MyModule_foo`.
Query
let mytext = 'welcome'
let myQuery = graphql`query($text: String = "${mytext}") {
allGhostPost : allGhostPost(filter:{title:{eq: $text}}) {
edges {
node {
id
slug
}
}
}
}`
Please help!!!
Inserting arbitrary text into queries like this is a well-known security issue and the Babel plugin is almost certainly right to forbid it. GraphQL defines a JSON-over-HTTP payload format that allows passing the variables separately (encoded as JSON objects to minimize the possibility of injection attacks).
You don't show what's actually making the query, but it should have a place to add a map of GraphQL variables. (For example, the graphql-js reference implementation includes a variableValues parameter to its top-level graphql function.) Remove the = "${mytext}" part of the query, and instead use a variables object like {text: mytext}.

Resources