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

Seeding data

This guide will show you how to create a User list and add initial data to it using the createItems function. This process is also called seeding.

Note: In a previous chapter the code was split up over separate files, while this is preferred in a real code base, in this part everything is put in one file for clarity reasons.

List setup

Install packages

This chapter will use a different User schema than previous chapters and instead of a Todo list, there will be a Post list. It is best to start from a fresh project and start from an empty database (delete data from previous chapters). Also, make sure to have all of the following packages installed:

yarn add @keystonejs/keystone
yarn add @keystonejs/adapter-mongoose
yarn add @keystonejs/app-graphql
yarn add @keystonejs/fields
yarn add @keystonejs/app-admin-ui
yarn add @keystonejs/auth-password
yarn add @keystonejs/server-side-graphql-client

Preparation

First let's create a User list and add a PasswordAuthStrategy. The code in index.js:

JS
const { Keystone } = require('@keystonejs/keystone');
const { PasswordAuthStrategy } = require('@keystonejs/auth-password');
const { Text, Checkbox, Password } = require('@keystonejs/fields');
const { GraphQLApp } = require('@keystonejs/app-graphql');
const { AdminUIApp } = require('@keystonejs/app-admin-ui');
const { MongooseAdapter } = require('@keystonejs/adapter-mongoose');

const keystone = new Keystone({
  adapter: new MongooseAdapter({ mongoUri: 'mongodb://localhost/keystone' }),
});

keystone.createList('User', {
  fields: {
    name: { type: Text },
    email: { type: Text, isUnique: true },
    isAdmin: { type: Checkbox },
    password: { type: Password },
  },
});

const authStrategy = keystone.createAuthStrategy({
  type: PasswordAuthStrategy,
  list: 'User',
});

module.exports = {
  keystone,
  apps: [
    new GraphQLApp(),
    new AdminUIApp({ name: 'example-project', enableDefaultRoute: true, authStrategy }),
  ],
};

Tip: A similar setup can be achieved by running the Keystone CLI yarn create keystone-5-app and selecting Starter (Users + Authentication). This starter project has a User list, PasswordAuthStrategy and seeding of the database already configured. For now, we will proceed manually.

Creating items

The createItems utility function requires a config object argument. It has the following required keys:

  • keystone: a Keystone instance
  • listKey: the Keystone list name
  • items: the array of objects to be created.
JS
const { createItems } = require('@keystonejs/server-side-graphql-client');

await createItems({
  keystone,
  listKey: 'User',
  items: [
    {
      data: {
        name: 'John Duck',
        email: 'john@duck.com',
        password: 'dolphins',
      },
    },
    {
      data: {
        name: 'Barry',
        email: 'bartduisters@bartduisters.com',
        password: 'dolphins',
      },
    },
  ],
});

Note: The format of the objects in the items array must align with the schema setup of the corresponding list name. As an example in our schema, the email field has isUnique:true constraint, therefore it would not be possible for the above code to generate users with exactly same email.

Example on how to seed the data upon database connection:

JS
const { createItems } = require('@keystonejs/server-side-graphql-client');

const keystone = new Keystone({
  adapter: new MongooseAdapter({ mongoUri: 'mongodb://localhost/keystone' }),
  onConnect: async keystone => {
    // Reset the database each time for this example
    await keystone.adapter.dropDatabase();
    await createItems({
      keystone,
      listKey: 'User',
      items: [
        {
          data: {
            name: 'John Duck',
            email: 'john@duck.com',
            password: 'dolphins',
          },
        },
        {
          data: {
            name: 'Barry',
            email: 'bartduisters@bartduisters.com',
            password: 'dolphins',
          },
        },
      ],
    });
  },
});

Start the application and visit the Admin UI, two users are available on startup. You can log in to the Admin UI with the email john@duck.com and password dolphins.

Note: In this example the same two users would be generated every startup. Since email should be unique, this would cause a duplicate error to show up. To avoid this, we clear the database before starting Keystone each time.

Relationships

The items in the createItems config object has the data type of GraphQL [listKey]sCreateInput. In our example, it's the UsersCreateInput which keystone created for us as part of the schema.

Consequently, while seeding it's possible to create relationships between items using keystone connect nested mutations.

Single relationships

Add the Relationship field to the imports:

JS
const { Text, Checkbox, Password, Relationship } = require('@keystonejs/fields');

Create a list with a relationship to another list:

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

As part of connect nested mutation, we need to provide the id of the item for which the single relationship is required. This implies that we need to extract the id of the previously created items.

Example on how to seed an item with a relationship using connect nested mutation:

JS
await createItems({
  keystone,
  listKey: 'Post',
  items: [
    {
      data: {
        title: 'Hello World',
        author: {
          // Extracting the id from `users` array
          connect: { id: users.find(user => user.name === 'John Duck').id },
        },
      },
    },
  ],
});

The full example:

JS
const keystone = new Keystone({
  adapter: new MongooseAdapter({ mongoUri: 'mongodb://localhost/keystone' }),
  onConnect: async keystone => {
    // Reset the database each time for this example
    await keystone.adapter.dropDatabase();

    // 1. Insert the user list first to obtain the user IDs
    const users = await createItems({
      keystone,
      listKey: 'User',
      items: [
        {
          data: {
            name: 'John Duck',
            email: 'john@duck.com',
            password: 'dolphins',
          },
        },
        {
          data: {
            name: 'Barry',
            email: 'bartduisters@bartduisters.com',
            password: 'dolphins',
          },
        },
      ],
      returnFields: 'id, name',
    });

    // 2. Insert `Post` data, with the required relationships, via `connect` nested mutation.
    await createItems({
      keystone,
      listKey: 'Post',
      items: [
        {
          data: {
            title: 'Hello World',
            author: {
              // Extracting the id from `users` array
              connect: {
                id: users.find(user => user.name === 'John Duck').id,
              },
            },
          },
        },
      ],
    });
  },
});

Clear the database, then start Keystone and visit the Admin UI to see that two users are generated and one post is generated. The post has an author named John Duck. In the database author will be the ID of the user with name John Duck

Many relationships

In general, a user can have many posts. We can set up our User list to refer to Posts with a one to many relationship by adding a posts field

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

and then updating our Post.author field to refer to User.posts.

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

Note that User.posts has { many: true }, while Post.author has { many: false }.

Following the same pattern as discussed above, we can easily establish a to-many relationship via connect nested mutations approach. Instead of passing a single item id, we are required to pass an array of item ids.

Note: We need to create posts first as we're going to use the post ids to create to-many relationship with user items.

To associate all the posts where title contains the word React:

In action:

JS
const keystone = new Keystone({
  adapter: new MongooseAdapter({ mongoUri: 'mongodb://localhost/keystone' }),
  onConnect: async keystone => {
    // Reset the database each time for this example
    await keystone.adapter.dropDatabase();

    // 1. Create posts first as we need generated ids to establish relationship with user items.
    const posts = await createItems({
      keystone,
      listKey: 'Post',
      items: [
        { data: { title: 'Hello Everyone' } },
        { data: { title: 'Talking about React' } },
        { data: { title: 'React is the Best' } },
        { data: { title: 'Keystone Rocks' } },
      ],
      returnFields: 'id, title',
    });

    // 2. Insert User data with required relationship via nested mutations. `connect` requires an array of post item ids.
    await createItems({
      keystone,
      listKey: 'User',
      items: [
        {
          data: {
            name: 'John Duck',
            email: 'john@duck.com',
            password: 'dolphins',
            posts: {
              // Filtering list of items where title contains the word `React`
              connect: posts
                .filter(post => /\bReact\b/i.test(post.title))
                .map(post => ({ id: post.id })),
            },
          },
        },
        {
          data: {
            name: 'Barry',
            email: 'bartduisters@bartduisters.com',
            password: 'dolphins',
            isAdmin: true,
          },
        },
      ],
    });
  },
});

Start the Keystone application and visit the Admin UI. Take a look at the user John Duck, he has two posts associated with him (there were two posts with the word React in the title).

If you want to explore other utility functions for CRUD operations, please refer to server-side GraphQL client API for more details.

On this page

Edit on GitHub