Skip to content
KeystoneJS LogoKeystoneJS
👋🏻 Keystone 5 has officially moved to maintenance only. For the latest release of Keystone please visit the Keystone website.

Configuring relationships

Keystone allows you to model your data by declaring relationships between lists. There are a number of different ways you can configure relationships, depending on how you want to model your data and how you need to access it from the graphQL API. This guide will step you through the decision making process to help you get all your configurations just right.

Throughout this guide we will use the example of a blog application which has Users and Posts.

One-sided vs two-sided

A relationship in Keystone exists between two lists. In our blog, the concept of authorship (who wrote a post) can be represented as a relationship between the User and the Post lists.

The first question you need to consider is which list do you want to be able to access the relationship from in your graphQL API? In our blog we might want to be able to ask about a user's posts, a post's author, or possibly both. If you only need to access one side of the relationship then you want to configure a one-sided relationship. If you need both, then you want to configure a two-sided relationship.

Let's assume that each post in our blog has a single author and look at how we would use the ref option to configure both a one-sided and two-sided relationship.

One-sided

If we want to know who the author of a post is, but we're not interested in querying for all the posts written by a given user we can set up a one-sided relationship as follow:

JS
keystone.createList('User', { fields: { name: { type: Text } } });

keystone.createList('Post', {
  fields: {
    title: { type: Text },
    content: { type: Text },
    author: { type: Relationship, ref: 'User', many: false },
  },
});

In the author field we have specified ref: 'User' to indicate that this field relates to an item in the User list. We can now write the following query to find the author for each post:

GraphQL
Query {
  allPosts {
    title
    content
    author {
      name
    }
  }
}

Two-sided

If we also want to access all the posts written by a user then we need to use a two-sided relationship.

JS
keystone.createList('User', {
  fields: {
    name: { type: Text },
    posts: { type: Relationship, ref: 'Post.author', many: true },
  },
});

keystone.createList('Post', {
  fields: {
    title: { type: Text },
    content: { type: Text },
    author: { type: Relationship, ref: 'User.posts', many: false },
  },
});

In this case we have a Relationship field on both lists, and they both have a ref config in the form <listName>.<fieldName>. These Relationship fields represent the two sides of the relationship, and we can now use the following graphQL query as well:

GraphQL
Query {
  allUsers {
    name
    posts {
      title
      content
    }
  }
}

Cardinality

The second question we need to ask is what the cardinality of our relationship should be. The cardinality of a relationship is the number items which can exist on either side of the relationship. In our blog do we want each post to have exactly one author, or can it have multiple authors? Are users allowed to write more than one post or do we want to restrict them to exactly one post each for some reason? The answers to these questions will give us the cardinality of our relationship.

There are three types of cardinality, one-to-many, many-to-many, and one-to-one, and they can be configured using the many config option.

One-to-many

If we want a blog where each post can have one author, and each user can be the author of many posts, then we have a one-to-many relationship. We can configure a one-sided version of this:

JS
keystone.createList('User', { fields: { name: { type: Text } } });

keystone.createList('Post', {
  fields: {
    title: { type: Text },
    content: { type: Text },
    author: { type: Relationship, ref: 'User', many: false },
  },
});

or a two-sided version:

JS
keystone.createList('User', {
  fields: {
    name: { type: Text },
    posts: { type: Relationship, ref: 'Post.author', many: true },
  },
});

keystone.createList('Post', {
  fields: {
    title: { type: Text },
    content: { type: Text },
    author: { type: Relationship, ref: 'User.posts', many: false },
  },
});

Note that we have used many: false in the author field and many: true in the posts field.

Many-to-many

If we want a blog where each post can have many authors, and each user can be the author of many posts, then we have a many-to-many relationship. We can configure a one-sided version of this:

JS
keystone.createList('User', { fields: { name: { type: Text } } });

keystone.createList('Post', {
  fields: {
    title: { type: Text },
    content: { type: Text },
    authors: { type: Relationship, ref: 'User', many: true },
  },
});

or a two-sided version:

JS
keystone.createList('User', {
  fields: {
    name: { type: Text },
    posts: { type: Relationship, ref: 'Post.authors', many: true },
  },
});

keystone.createList('Post', {
  fields: {
    title: { type: Text },
    content: { type: Text },
    authors: { type: Relationship, ref: 'User.posts', many: true },
  },
});

Note that we have used many: true in both the authors and posts fields.

One-to-one

If we want a blog where each post has exactly one author, and each user is restricted to writing exactly one post, then we have a one-to-one relationship. In this case we can only specify this with a two-sided relationship:

JS
keystone.createList('User', {
  fields: {
    name: { type: Text },
    post: { type: Relationship, ref: 'Post.author', many: false },
  },
});

keystone.createList('Post', {
  fields: {
    title: { type: Text },
    content: { type: Text },
    author: { type: Relationship, ref: 'User.post', many: false },
  },
});

Note that we have used many: false in both the authors and posts fields.

Summary

When configuring a relationship in Keystone there are two key questions you need to answer:

  • Do I want a one-sided or two-sided relationship?
  • What is the cardinality of my relationship?

Once you know the answers to these questions you can configure your relationship using the ref and many options.

On this page

Edit on GitHub