Given the graphql union type
union SearchResult = Human | Droid | Starship
and the following graphql query:
{
search(text: "an") {
... on Human {
... on SearchResult {
... on Droid {
appearsIn
}
}
}
}
}
Why is it in graphql syntactically correct to go from a "sub type" inline fragment (Human) to a "super type" inline fragment (SearchResult) and back to another "sub type" inline fragment (Droid)? Are there any use cases for this?
For me this really does not make sense and it should throw an error. It leads to dead query parts like the Droid inline fragment above that has no effect on the query result.
Without this rule, named and inline fragments can behave the same way, and this can make sense if you have more involved "get generic bits" fragments for interface or union types. If you split out the query this way:
fragment SearchResultBits on SearchResult {
...on Droid { appearsIn }
# could have parts for other union choices too
}
query DoSearch($text: String!) {
search(text: $text) { ... SearchResultBits }
}
Then on your query it's very sensible to reuse the (named) SearchResultBits
query FromQuestion {
search(text: "an") {
... on Human {
name
... SearchResultBits
}
# or return nothing for non-humans
}
}
Since the named fragment reference and the inline fragment both behave the same way, you'll get the same result here. It seems more reasonable for this named fragment reference to be legal and so there doesn't seem to be a strong reason to forbid the equivalent inline fragment either.
Related
I am new to Graphql. I go through the grapql docs https://graphql.org/learn/queries/#inline-fragments
query HeroForEpisode($ep: Episode!) {
hero(episode: $ep) {
name
... on Droid {
primaryFunction
}
... on Human {
height
}
}
}
what is Droid and Human here?
They are GraphQL object type names.
The corresponding GraphQL SDL is in the Interfaces section of the "Schemas and Types" page. The minimum interesting part of the schema is
interface Character { ... }
type Human implements Character { ... }
type Droid implements Character { ... }
So in this query the hero field has the interface type Character, and the ... on SomeTypeName syntax lets you pick out the fields from a specific implementation type if the actual value is that type.
I am using Prismic, and I have two identical custom types, one is called Content and one is called Theme. Their data is identical so I would like to reuse my fragments, is it possible?
An example fragment looks like:
import { graphql } from 'gatsby'
export const CollectionFragment = graphql`
fragment CollectionFragment on PrismicContentBodyCollection {
...
}
`
So right now it is hardcoded to PrismicContentBodyCollection.
A GraphiQL example would look like:
query MyQuery {
allPrismicTheme {
nodes {
data {
body {
... on PrismicThemeBodyHero {
slice_type
}
}
}
}
}
allPrismicContent {
nodes {
data {
body {
... on PrismicContentBodyHero {
slice_type
}
}
}
}
}
}
I'm not 100% sure, but I don't think this is possible because it needs to be specified with the type that matches the type of document you're looking for to make sure that your query is valid and that the fields you are trying to receive actually exist on the object.
So in your case, if you're looking for the Collection Slice, the fragments would need to be
PrismicThemeBodyCollection and PrismicContentBodyCollection respectively.
I have made a few tests myself and I keep getting errors that say I'm missing the correct content type name
I am trying to mimic what some GraphQL does, but I do not have access to be able to run the original. It is of the form:
query {
dataSources(dataType: Ais) {
... on AisDataSource {
messages(filter: {broadcastType: Static}) {
... on AisStaticBroadcast {
field1
field2
(I have ommitted the closing parentheses).
It is my understanding that ... on is either to include a fragment (none here), or to choose between alternatives (but these are nested). So is this query wrong, or is there more to ... on?
This
{
user {
... on User {
id
username
}
}
}
and this
{
user {
...UserFragment
}
}
fragment UserFragment on User {
id
username
}
are equivalent. In both cases, you are using a fragment. In the first example, we simply refer to the fragment as an inline fragment.
When requesting a field that return a composite type (an object, interface or union), you must specify a selection set, or one or more fields for the return type. Since fragments must include a type condition (the on keyword plus the type name), they can be used to specify different selection sets depending on the type that's actually returned at runtime.
{
user {
...RegularUserFragment
...AdminFragment
}
}
fragment RegularFragment on RegularUser {
id
username
}
fragment AdminFragment on Admin {
id
username
accessLevel
}
All we're saying is "if the type at runtime is this, then return this set of fields". If any of the fields inside the fragment also return a composite type, then those fields also have to specify a selection set for -- that means additional fragments can be used inside those selection sets.
I am looking at GraphQL but am confused why when using a fragment as below, you have to define the "on Character"? could this be anything any name as doesn't explain or have the context on the GraphQL documentation.
query {
leftComparison: hero(id: "1") {
...comparisonFields
}
rightComparison: hero(id: "2") {
...comparisonFields
}
}
fragment comparisonFields on Character {
name
appearsIn
friends {
name
}
}
While the example on graphql.org doesn't make this totally obvious, a fragment is always attached to some specific type (can be an object type, interface, or union). Inside the fragment, you can only use fields that exist on the type that's named; the server will check this for you (and clients are capable of checking ahead of time if they want to).
If a field returns an interface or union type, you can similarly only select fields that you know to exist (because an interface provides them), but you can attempt to match on specific types that implement the interface or are members of the union to get more data. This is frequently done with inline fragments, but since a named fragment is attached to a type, you can use named fragments as well. If the schema contains the very generic query
interface Node { id: ID! }
type Query {
node(id: ID!): Node
}
and Character implements Node, then you can plug in the named fragment you have here
query GetCharacterDetails($id: ID!) {
node(id: $id) {
...comparisonFields
}
}
My CMS returns me a list of nodes each having its own nodetype. For each nodetype I have a corresponding GraphQL type defined.
type getContent {
content: [ContentNode]
}
I want a query like:
{
content{
contentType
properties {
${ContentType.getFragment('content', type: $contentType???)}
}
}
}
ContentType would return a correct fragment definition based on type variable provided to it. But how do I get $contentType from parent results?
You can't have fragments that depend on actual value of the parent, because fragments are composed before the query request is actually made to server. There are two different ways to handle this, one is to have fragment vary based on variables and other is to use interface and typed fragments inside your component.
Here is a good answer showing example of using variables: Conditional fragments or embedded root-containers when using Relay with React-Native
For the interface solution, if you have ContentNode an interfaces with implementations like 'ContentNode1' and 'ContentNode2', then you can do something like that:
{
content {
${ContentType.getFragment('content')}
}
}
And in your ContentType component
fragment on ContentNode {
contentType
someOtherCommonField
... on ContentNode1 {
someContent1Field
}
... on ContentNode2 {
someContent2Field
}
}