Relationships

Without relationships, your type definitions are simply a collection of disconnected nodes, with little value. Adding relationships into your data model gives your data the context that it needs to run complex queries across wide sections of your graph. This section will run through writing some type definitions for a simple connected model, inserting some data through the schema, and then querying it.

1. Example graph

We will be using the following example graph, where a Person type has two different relationship types which can connect it to a Movie type.

relationships
Figure 1. Example graph

2. Type definitions

First, we should define the two distinct types in this model:

type Person {
    name: String!
    born: Int!
}

type Movie {
    title: String!
    released: Int!
}

We can then connect these two types together using @relationship directives:

type Person {
    name: String!
    born: Int!
    actedInMovies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT)
    directedMovies: [Movie!]! @relationship(type: "DIRECTED", direction: OUT)
}

type Movie {
    title: String!
    released: Int!
    actors: [Person!]! @relationship(type: "ACTED_IN", direction: IN)
    director: Person! @relationship(type: "DIRECTED", direction: IN)
}

The following should be noted about the fields we just added:

  • A Person can act in or direct multiple movies, and a Movie can have multiple actors. However, it is exceedingly rare for a Movie to have more than one director, and we can model this cardinality in our type definitions, to ensure accuracy of our data.

  • A Movie isn’t really a Movie without a director, and we have signified this by marking the director field as non-nullable, meaning that a Movie must have a DIRECTED relationship coming into it.

  • To figure out whether the direction argument of the @relationship directive should be IN or OUT, visualise your relationships like in the diagram above, and model out the direction of the arrows.

3. Inserting data

Nested mutations mean that there are many ways in which we can insert data into our database through the GraphQL schema. We know that we can’t create a Movie without adding a director, and we can do that by either creating the director first and then creating and connecting the movie, or we can create both the Movie and the director in the same mutation. Let’s try the latter:

mutation CreateMovieAndDirector {
    createMovies(input: [
        {
            title: "Forrest Gump"
            released: 1994
            director: {
                create: {
                    name: "Robert Zemeckis"
                    born: 1951
                }
            }
        }
    ]) {
        movies {
            title
            released
            director {
                name
                born
            }
        }
    }
}

We then need to create the actor in our example, and connect them to the new Movie node:

mutation CreateActor {
    createActors(input: [
        {
            name: "Tom Hanks"
            born: 1956
            movies: {
                connect: {
                    where: {
                        title: "Forrest Gump"
                    }
                }
            }
        }
    ]) {
        movies {
            title
            released
            director {
                name
                born
            }
            actors {
                name
                born
            }
        }
    }
}

As you can see, these nested mutations are very powerful, and in the second Mutation we ran, we were able to return the entire graph which was created in this example. In fact, these mutations can actually be compressed down into a single Mutation which inserts all of the data we need:

mutation CreateMovieDirectorAndActor {
    createMovies(input: [
        {
            title: "Forrest Gump"
            released: 1994
            director: {
                create: {
                    name: "Robert Zemeckis"
                    born: 1951
                }
            }
            actors: {
                create: [
                    {
                        name: "Tom Hanks"
                        born: 1956
                    }
                ]
            }
        }
    ]) {
        movies {
            title
            released
            director {
                name
                born
            }
            actors {
                name
                born
            }
        }
    }
}

Once you get your head around this, you’ll be creating giant sub-graphs in one Mutation in no time!

4. Fetching our data

Now that we have our Movie information in the database, we can query all of the information which just inserted as follows:

query {
    movies(where: { title: "Forrest Gump" }) {
        title
        released
        director {
            name
            born
        }
        actors {
            name
            born
        }
    }
}