8

This past year I converted an application to use Graphql. Its been great so far, during the conversion I essentially ported all my services that backed my REST endpoints to back grapqhl queries and mutations. The app is working well but would like to continue to evolve my object graph.

Lets consider I have the following relationships.

User -> Team -> Boards -> Lists -> Cards -> Comments

I currently have two different nested schema: User -> team:

    type User {
  id: ID!
  email: String!
  role: String!
  name: String!
  resetPasswordToken: String
  team: Team!
  lastActiveAt: Date
}

type Team {
  id: ID!
  inviteToken: String!
  owner: String!
  name: String!
  archived: Boolean!
  members: [String]
}

Then I have Boards -> Lists -> Cards -> Comments

type Board {
  id: ID!
  name: String!
  teamId: String!
  lists: [List]
  createdAt: Date
  updatedAt: Date
}

type List {
  id: ID!
  name: String!
  order: Int!
  description: String
  backgroundColor: String
  cardColor: String
  archived: Boolean
  boardId: String!
  ownerId: String!
  teamId: String!
  cards: [Card]
}

type Card {
  id: ID!
  text: String!
  order: Int
  groupCards: [Card]
  type: String
  backgroundColor: String
  votes: [String]
  boardId: String
  listId: String
  ownerId: String
  teamId: String!
  comments: [Comment]
  createdAt: Date
  updatedAt: Date
}

type Comment {
  id: ID!
  text: String!
  archived: Boolean
  boardId: String!
  ownerId: String
  teamId: String!
  cardId: String!
  createdAt: Date
  updatedAt: Date
}

Which works great. But I'm curious how nested I can truly make my schema. If I added the rest to make the graph complete:

type Team {
      id: ID!
      inviteToken: String!
      owner: String!
      name: String!
      archived: Boolean!
      members: [String]
      **boards: [Board]**
    }

This would achieve a much much deeper graph. However I worried how much complicated mutations would be. Specifically for the board schema downwards I need to publish subscription updates for all actions. Which if I add a comment, publish the entire board update is incredibly inefficient. While built a subscription logic for each create/update of every nested schema seems like a ton of code to achieve something simple.

Any thoughts on what the right depth is in object graphs? With keeping in mind the every object beside a user needs to be broadcast to multiple users.

Thanks

sauce
  • 592
  • 4
  • 9
  • 25
  • Who is consuming your API? It sounds like it's a single client that you are also developing. If you know what client or clients are using your API, do those clients need the functionality provided by adding, for example, a `boards` field on the `Team` type? – Daniel Rearden Dec 31 '18 at 12:30
  • "Which if I add a comment, publish the entire board update is incredibly inefficient." Can you clarify why you're publishing the entire board when adding a comment? I would imagine adding a comment should only result in publishing to some kind of `commentAdded` subscription. If `card` has a `comments` field, the client should take care of updating that field using `writeQuery` rather than relying on the `card` being published. Am I missing something? – Daniel Rearden Dec 31 '18 at 12:43

2 Answers2

4

GraphQL's purpose is to avoid a couple of queries, so I'm sure that making the nested structure is the right way. With security in mind, add some GraphQL depth limit libraries.

GraphQL style guides suggest you have all complex structures in separate Object Types ( as you have, Comment, Team, Board... ). Then making a complex query/mutation is up to you.

I'd like you to expand this sentence

Which if I add a comment, publish the entire board update is incredibly inefficient

I'm not sure about this as you have your id of the Card. So adding new comment will trigger mutation which will create new Comment record and update Card with the new comment.

So your structure of data on the backend will define the way you fetch it but not so much the way you mutate it.

Take a look at the GitHub GraphQL API for example:

  • each of the mutations is a small function for updating/creating piece of the complex tree even if they have nested structure of types on the backend.

In addition for general knowledge of what are approaches for designing the mutations, I'd suggest this article.

Yevhenii Herasymchuk
  • 2,047
  • 15
  • 20
  • 1
    Not entirely sure why this didn't update me, sorry for the delayed response. The article you posted about Modeling GraphQL Mutations was a really good read. Makes a lot of sense... I was actually just sending a JSON type for updates because I didn't want to write a mutation per field. I guess to conclude though, the biggest thing that feels like boiler plate to me is the subscriptions part, I can have this amazing queryable object graph, but needing to make essentially 2 subscription handlers per object feels odd. Maybe some frameworks help, wish could pass an option on a mutation or something – sauce Jan 06 '19 at 20:44
1

You can use nesting in GraphQL like

type NestedObject {
  title: String
  content: String
}

type MainObject {
  id: ID!
  myObject: [NestedObject]
}

In the above code, the type definition of NestObject gets injected into the myObject array. To understand better you can see it as:

type MainObject {
  id: ID!
  myobject: [
    {
      title: String
      content: String
    }
  ]
}

I Hope this solves your problem!

Yogesh Aggarwal
  • 1,071
  • 2
  • 12
  • 30