henkimon
henkimon

Reputation: 1481

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]
}

Upvotes: 7

Views: 3482

Answers (3)

Marco Lüthy
Marco Lüthy

Reputation: 1309

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

Upvotes: 4

su27
su27

Reputation: 1165

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.

Upvotes: 0

Petr Bela
Petr Bela

Reputation: 8741

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
    }
  }
}

Upvotes: 0

Related Questions