defining a GraphQL mutation in DGraph that adds a node that is of union type within another node - graphql

I am new to GraphQL and Dgraph and have been reading through the manual.
I am following an example in the manual posted here:
https://dgraph.io/docs/graphql/schema/types/#union-type
I am seeking to use a mutation to add a node or list of Nodes and associate them as members acceptable within the addHome input function.
These nodes are however also of type Union and so I need to somehow cast the HomeMember to a Dog and define the category as an animal.
I see someone posted something here but i can't pull the pieces together since the code it suggests appears to already have been generated by DGraph:
https://discuss.dgraph.io/t/union-types-in-graphql/9581
I am unsure but have a feeling I perhaps need to somehow override the input HomeMemberRef which is automatically generated into the schema by DGraph and disable both parrot and human ?
My code so far looks like this:
mutation {
addHome(input: [
{address: "London",
// Here I need to add something a member that casts the
// HomeMember to a Dog, defines the category as an animal and is accepted within
// this addHome input function
}
]) {
home{
address
id
members
}
}
}

Found the answer posted on another page:
https://dgraph.io/docs/graphql/mutations/mutations-overview/
My GraphQL works using this:
mutation {
addHome(input: [
{address: "London",
members: [
{ dogRef: { category: Mammal, breed: "German Shepherd"} },
]
}
]) {
home {
address
members {
... on Dog {
breed
}
... on Parrot {
repeatsWords
}
... on Human {
name
}
}
}
}
}

Related

Is there a way to pass a parameter into a GraphQL query to specify the GraphQL type it should be run on?

I'm new to GraphQL and would like to be able to use a variable for a GraphQL name in a query.
I've attempted to use the standard $ syntax but with no luck.
Working query:
query Tryptych($section: SectionsEnum = home) {
enGB: entries(section: [$section], site: "enGB") {
... on Home {
tryptych {
...tryptychFields
}
}
}
}
What I'd like to be able to do:
query Tryptych($section: SectionsEnum = home, $interface: SomeType = Home) {
enGB: entries(section: [$section], site: "enGB") {
... on $interface {
tryptych {
...tryptychFields
}
}
}
}
Fragment for reference:
fragment tryptychFields on TryptychTryptych {
__typename
theme
tagline
firstImageTitle
firstImageContent
firstImageAsset {
url
}
firstImageLink
secondImageTitle
secondImageContent
secondImageAsset {
url
}
secondImageLink
thirdImageTitle
thirdImageContent
thirdImageAsset {
url
}
thirdImageLink
}
In the code snippet for what I'd like to achieve I get the error message:
Expected Name, found $
Thanks for the help.
A variable can only have one type, that type must be an input type (i.e. a scalar, enum or input object type), and it can only be used where an input type would be expected (i.e. a field or directive argument). In other words, the syntax you're suggesting is not supported.
If you have multiple types that may be returned by the same field, you may use any number of fragments to specify the selection set by type. The actual selection set will be determined at runtime when the type of the field is evaluated. For example, if the animal field returns a union of Cat, Dog and Bird types:
query {
animal {
... on Cat {
meows
}
... on Dog {
barks
}
... on Bird {
chirps
}
}
}
You may also use the #skip and #include directives to control which fields are selected:
query ($inAHouse: Boolean!, $withAMouse: Boolean!) {
greenEggs #skip(if: $inAHouse)
ham #include(if: $withAMouse)
}
And you may include multiple operations in a single document, and then specify an operationName with your request to tell the server which operation to run:
query OperationA {
foo
}
query OperationB {
bar
}

Validation error of type UndefinedFragment: Undefined fragment

I've a graphql-jave v8.0 app running on Spring boot v1.5.10, and I'm trying to utilize 'fragment' feature of GraphQL to fetch limited number of fields with the following schema type definition:
type School {
id: ID
name: String
address: String
age: String
jobTitle: String
...
}
fragment UserFields on School {
age
jobTitle
}
type Query {
user (id: String!): School!
}
schema {
query: Query
}
When I execute this query:
{
user (id: "123")
{
... UserFields
}
}
The expected result should be:
{
"user": {
"age": "12",
"jobTitle": "student"
}
}
However, It results in the following error
"message": "Validation error of type UndefinedFragment: Undefined fragment
UserFields # 'user'",
Off course I can do this with explicitly passing the field name in the query but for the sake of example, I'm interested in utilizing fragment feature of GraphQL.
Any idea what I'm doing wrong please?
Fragments aren't defined in the schema, they're something for letting you create abstractions whilst building complex queries -- the main purpose is to allow you to avoid repetition when querying the same type in multiple parts of your query.
As Andrew said, and as the official docs exemplify, fragments are not defined in the schema but (ad hoc) in the query:
{
user (id: "123") {
... UserFields
}
}
fragment UserFields on School {
age
jobTitle
}
Unconditional fragments (like the one here) are used to avoid repetition. Imagine having multiple places where you want to select age and jobTitle in the same operation.
Conditional fragments, on the other hand, are used to make a conditional selection depending on the concrete interface implementation or union subtype.

GraphQL non nested relation

I'm trying to have a representation of nodes on GraphQL more akin to what jsonapi would be like http://jsonapi.org/
What I mean is if we take one of the examples on GraphQL
{
hero {
name
# Queries can have comments!
friends {
name
}
}
}
Have a representation that would be more along these lines
{
hero {
name
# Queries can have comments!
friends {
id
}
},
friends {
id, name
}
}
Is that at all possible in GraphQL
Thanks
It is possible, and there's nothing wrong with having a friends field. In GraphQL terms you can have the following part of the schema:
type User {
id: ID
name: String
firends: [User]
}
type RootQuery {
hero: User
friends(forUserId: ID!): [User]
}
And then you can query this as you like – you can ask for friends separately:
{
friends(forUserId: "12") {
id, name
}
}
But the whole idea of GraphQL is that you don't have to do multiple queries to get the information you need. If you just need a list of users – that's a reasonable query, that most people have (with arguments for pagination and so on). With that said, there's no reason to fetch a list of IDs and to send another fetch query for the data right after that.

GraphQL arguments to connection sub results

I'm need help passing arguments to collections/connections/arrays in GraphQL syntax.
I'm just learning it, playing with the SWAPI at http://graphql.org/swapi-graphql/
I can pass an id argument to a single type, like this:
query getANewHope {
film(id: "ZmlsbXM6MQ==") {
id
title
}
}
But I don't know how to query the results of a collection/connection
query starships {
allStarships(id: "c3RhcnNoaXBzOjI=") { # this doesn't work
edges {
node(id: "c3RhcnNoaXBzOjI=") { # ...nor this.
id
}
}
}
}
I want to query collections because, I'd like to connect the two ideas like "All Starfighter type ships in A New Hope"?
query filmStarships {
film(id: "ZmlsbXM6MQ==") {
title
starshipConnection { #How to limit this? I can't use (starshipClass: "Starfighter") here...
edges {
node { # ...nor here..
starshipClass # ...nor here.
}
}
}
}
}
query starships2 {
starship (id: "c3RhcnNoaXBzOjI=") { # This doesn't work either
id # even without an arugment abovce, it says "Unknown argument \"id\" on field \"node\" of type \"StarshipsEdge\"."
}
}
Arguments like you're asking for have to be implemented in the schema. The SWAPI does does not expose an argument to filter by starshipClass. Click Docs on the top right of the GraphiQL window to explore the provided schema.
If you are implementing your own schema it would be very easy to add a filter on starshipClass in the resolvers.

Structuring GraphQL types

I've run into an issue while trying to extend my API to include a GraphQL endpoint. The application I'm working on is a kind of forum with Messages. A message can contain comments of type Message. If a message is a comment it has a parent of type Message. Simplified, the schema looks like this:
type Message {
id: String
content: String
comments: [Message]
parent: Message
}
type RootQuery {
message(id: String): Message
messages: [Message]
}
The problem with this schema is that it allows for queries like this:
{
messages {
comments {
parent {
comments {
parent {
comments {
parent {
id
content
}
}
}
}
}
}
}
}
Keep in mind that I may want to allow for arbitrarily deep nesting of comments. In that case the following query should be allowed:
{
messages {
comments {
comments {
comments {
id
content
}
}
}
}
}
So, my question is this: Should I introduce a new type - Comment - to the API that do not know of its parent? Or are there any other ways of restricting this kind of unwanted behaviour?
Also, would the use of a Comment-type prohibit me from using the fragment messageFields on Message syntax in my queries? Perhaps this is the time to introduce interfaces to the schema?
Suggestion to a solution if I introduce the type Comment (I have not tried this):
interface Message {
id: String
content: String
comments: [Message]
}
type DefaultMessage : Message {
id: String
content: String
comments: [Comment]
parent: Message
}
type Comment : Message {
id: String
content: String
comments: [Message]
}
type RootQuery {
message(id: String): Message
messages: [Message]
}
Just in case anyone else ends up here wondering how to do recursive types in graphql-js, there's a useful hint in graphql-js's code:
* When two types need to refer to each other, or a type needs to refer to
* itself in a field, you can use a function expression (aka a closure or a
* thunk) to supply the fields lazily.
*
* Example:
*
* var PersonType = new GraphQLObjectType({
* name: 'Person',
* fields: () => ({
* name: { type: GraphQLString },
* bestFriend: { type: PersonType },
* })
* });
*
*/
https://github.com/graphql/graphql-js/blob/master/src/type/definition.js#L274
If a message is a comment it has a parent of type Message.
Looks like the parent field should be under type Comment, not DefaultMessage. That still wouldn't prevent a parent - comments - parent query but if you're worried about this for a DDOS reason, there are many other types of requests that are difficult to compute, even with REST APIs, and you should have other measures to detect such an attack.
Recursive nodes
However, you pose a very interesting question with the nested comments. How would you know how many times you need to nest the comment in the query to get all nested responses? I don't think it's currently possible with GraphQL to specify recursive objects.
I'd probably go around this limitation by fetching each nested comment one by one (or by X levels at a time) starting from the last comment as the node
{
messages {
comments {
id
content
}
}
}
followed by
{
node(commendId) {
comment {
id
content
}
}
}
I suppose you have a depth attribute of the Comment data structure, which should be pretty useful, for example, to limit the max nested depth when the users are posting comments.
So that your problem could be solved like this: in the resolver of the comments attribute, check the depth, return nothing if the depth is going illegal, otherwise fetch the comments and return.

Resources