To add relationships to a GraphQL API using the Neo4j GraphQL Library you use the @relationship
GraphQL schema directive.
GraphQL schema directives are GraphQL’s built-in extension mechanism that allows developers to indicate some custom server-side logic should occur.
To add a relationship to the GraphQL API you need to take the following steps:
-
Add a new field to represent the relationship
-
Define the type of this field (what type is on the other end of the relationship?)
-
Use the
@relationship
directive to define the relationship type (as stored in Neo4j) and the direction of the relationship (IN
orOUT
).
Let’s update the GraphQL type definitions to add the relationship between Movie
, Actor
and Genre
nodes.
Start by updating the type definitions in the GraphQL Toolbox to capture the relationships present in the property graph model above i.e. IN_GENRE
, ACTED_IN
and RATED
.
type Movie @node {
...
actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN)
genres: [Genre!]! @relationship(type: "IN_GENRE", direction: OUT)
}
type Actor @node {
...
movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT)
}
type Genre @node {
...
movies: [Movie!]! @relationship(type: "IN_GENRE", direction: IN)
}
The relationship fields are defined as non-nullable (e.g. [Actor!]!
) since if a relationship exists, it will always connect exactly two nodes, therefore, a relationship field can never be null.
Click to show the complete type definition
type Movie @node {
title: String!
year: Int
plot: String
imdbRating: Float
countries: [String]
languages: [String]
poster: String
revenue: Int
budget: Int
actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN)
genres: [Genre!]! @relationship(type: "IN_GENRE", direction: OUT)
}
type User @node {
userId: ID!
name: String!
}
type Actor @node {
name: String!
movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT)
}
type Genre @node {
name: String!
movies: [Movie!]! @relationship(type: "IN_GENRE", direction: IN)
}
Rebuild the schema to update the GraphQL schema and return to the query editor.
Finally, update the Matrix movie search query to traverse from the movie nodes to the actors and genres.
query MyQuery {
movies(where: { title_CONTAINS: "Matrix" }) {
title
actors {
name
}
genres {
name
}
}
}
Run the query and note how the actors and genres connected to each Matrix movie are returned.
{
"data": {
"movies": [
{
"title": "Matrix Reloaded, The",
"actors": [
{ "name": "Keanu Reeves" },
{ "name": "Carrie-Anne Moss" },
{ "name": "Christine Anu" },
{ "name": "Andy Arness" }
],
"genres": [
{ "name": "IMAX" },
{ "name": "Thriller" },
{ "name": "Action" },
{ "name": "Sci-Fi" },
{ "name": "Adventure" }
]
},
{
"title": "Matrix Revolutions, The",
"actors": [
{ "name": "Keanu Reeves" },
{ "name": "Mary Alice" },
{ "name": "Helmut Bakaitis" },
{ "name": "Kate Beahan" }
],
"genres": [
{ "name": "Action" },
{ "name": "IMAX" },
{ "name": "Thriller" },
{ "name": "Sci-Fi" },
{ "name": "Adventure" }
]
},
{
"title": "Matrix, The",
"actors": [
{ "name": "Hugo Weaving" },
{ "name": "Laurence Fishburne" },
{ "name": "Keanu Reeves" },
{ "name": "Carrie-Anne Moss" }
],
"genres": [
{ "name": "Thriller" },
{ "name": "Sci-Fi" },
{ "name": "Action" }
]
}
]
}
}
Click to explore how the generated Cypher query traverses the relationships
The Neo4j GraphQL library generates the following Cypher query for the above GraphQL query:
MATCH (this:`Movie`)
WHERE this.title CONTAINS $param0
CALL {
WITH this
MATCH (this)<-[this0:ACTED_IN]-(this1:`Actor`)
WITH this1 { .name } AS this1
RETURN collect(this1) AS var2
}
CALL {
WITH this
MATCH (this)-[this3:IN_GENRE]->(this4:`Genre`)
WITH this4 { .name } AS this4
RETURN collect(this4) AS var5
}
RETURN this { .title, actors: var2, genres: var5 } AS this
Params:
{
"param0": "Matrix"
}
Try and identify the following in the Cypher query above:
-
How the
actors
andgenres
fields are added to the Cypher query -
What relationship types are used to traverse from the
Movie
nodes to theActor
andGenre
nodes -
How the
actors
andgenres
fields are returned in the Cypher query
Relationship properties
Relations in Neo4j can have properties e.g. the ACTED_IN
relationship between an Actor
and a Movie
can have a roles
property that describes the role the actor played in the movie.
To add relationship properties to the GraphQL API you need to:
-
Add a type to the GraphQL schema that represents the
roles
relationship property:GraphQLtype ActedIn @relationshipProperties { role: String }
To find out more about interfaces in GraphQL see the GraphQL documentation. -
Modify the
actors
field in theMovie
type to pass theActedIn
type to theproperties
parameter of@relationship
directive.GraphQLtype Movie @node { actors: [Actor!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) }
-
Add the same
ActedIn
type to themovies
field in theActor
type:GraphQLtype Actor @node { movies: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) }
Click to show the complete type definition
type Movie @node {
title: String!
year: Int
plot: String
imdbRating: Float
countries: [String]
languages: [String]
poster: String
revenue: Int
budget: Int
actors: [Actor!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN)
genres: [Genre!]! @relationship(type: "IN_GENRE", direction: OUT)
}
type User @node {
userId: ID!
name: String!
}
type Actor @node {
name: String!
movies: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT)
}
type Genre @node {
name: String!
movies: [Movie!]! @relationship(type: "IN_GENRE", direction: IN)
}
type ActedIn @relationshipProperties {
role: String
}
Connection
fields generated for the relationship type e.g. the actorsConnection
of movies
field can be used to access the role
property of the ACTED_IN
relationship between an Actor
and a Movie
:
query MyQuery {
movies(where: {title_EQ: "Matrix, The"}) {
title
actors {
name
}
actorsConnection {
edges {
properties {
role
}
}
}
genres {
name
}
}
}
For more information on relationship properties, see the Neo4j GraphQL Library Relationship Properties documentation.
In the next lesson, your challenge is to add new relationships to the GraphQL type definitions.
Check Your Understanding
1. Relationships Schema Directive
What is the name of the GraphQL schema directive used to define relationships when using the Neo4j GraphQL Library?
Choose the correct answer.
-
❏
@relation
-
❏
@join
-
✓
@relationship
-
❏
@auth
Hint
GraphQL schema directives are GraphQL’s built-in extension mechanism and allow the API developer to add "annotations" on types and fields in the GraphQL type definitions.
Solution
The @relationship
GraphQL schema directive is used to define relationships.
2. Relationship Fields
True or False - relationship fields should always be defined as non-nullable when using the Neo4j GraphQL Library.
-
✓ True
-
❏ False
Hint
Relationships must be two-way; if a relationship field is nullable, a relationship could connect to only one node.
Solution
True - Relationship fields should always be non-nullable because if a relationship exists, it will always connect exactly two nodes
Summary
In this lesson, you learned how to add relationships to the GraphQL type definitions. You used the @relationship
directive to define the relationship type and direction. You also learned how to add relationship properties to the GraphQL API.