One to Many Relationships in GraphQL & Prisma

In this post we’ll talk about one-to-many relationships and how to implement them in GraphQL using Prisma.

Get the source code

In GraphQL it’s best practice to do “Schema driven development” meaning we do our schema first. In order to add relationships between tables, the first thing we’ll need to do is update our database schema. A quick refresher on relationships can be seen below:

the “many” side of the relationship (in our case todos) will have an ID of the “one” side of the relationship in its table. Todos each have a list that they belong to (the list ID).

Setup List without relations:

Okay, let’s create our List model in our schema.prisma file without the relations setup:

model List {
    id        Int      @id @default(autoincrement())
    createdAt DateTime @default(now())
    name      String
}

Now generate the newly updated database with the terminal commands below:

$ npx prisma migrate save --name "list-model" --experimental
$ npx prisma migrate up --experimental
$ npx prisma generate

Now we need the ability to create, view, and delete lists. We’ve already done this in previous tutorials so let’s blow through it quickly:

in schema.graphql, add your query, mutation, and type definition:

type Query {
  //...
  lists: [List!]!
  list(listId: ID!): List
}

type Mutation {
  //...
  createList(name: String!): List!
  deleteList(listId: ID!): List
}

type List {
  id: ID!
  name: String!
}

Now let’s add our query & mutation resolvers for the list to index.js:

const resolvers = {
  Query: {
    //...,
    lists: (parent, args, context, info) => {
      return context.prisma.list.findMany();
    },
    list: (parent, args, context, info) => {
      return context.prisma.list.findOne({
        where: {
          id: parseInt(args.listId),
        },
      });
    },

  },
  Mutation: {
    //...
    createList: (parent, args, context, info) => {
      return context.prisma.list.create({
        data: {
          name: args.name,
        },
      });
    },
    deleteList: (parent, args, context, info) => {
      return context.prisma.list.delete({
        where: { id: parseInt(args.listId) },
      });
    },
  }

}

Okay let’s fire up the server and test out our changes in the playground: Create two lists:

mutation {
  createList(name: "shopping") {
    name
    id
  }
}

Now make sure we can query for those lists:

query {
  lists {
    id
    name
  }
}

And make sure you can query a single list:

query {
  list(listId: 2) {
    id
    name
  }
}

And delete a list:

mutation {
  deleteList(listId: 1){
    id
    name
  }
}

If everything works then we’re ready to add todos into the mix.

Make sure you DELETE all your todos before continuing. Our current Todos don’t have a list associated with them, and if we run a migration that would cause existing data to be invalid it will fail.

Setting up a One To Many Relationship in GraphQL & Prisma

A one to many between todos and Lists would require each Todo to have a listId field to describe which list that todo belongs to. In order to set this up in Prisma, we’ll modify our schema.prisma to have the following:

model Todo {
    id          Int     @id @default(autoincrement())
    name        String
    isCompleted Boolean
    order       Int?
    list        List    @relation(fields: [listId], references: [id])
    listId      Int
}

model List {
    id        Int      @id @default(autoincrement())
    createdAt DateTime @default(now())
    name      String
    todos     Todo[]
}

The “many” side (Todo) will need to have a listId used to reference the List that it belongs to. It also needs to have a reference to the List object itself, which is where you define the foreign key.

list        List    @relation(fields: [listId], references: [id])

The line above just says “the field listId is equal to the id field in the List table“.

Then in the List model we just need to say there is a list of Todo records available. (Not sure why this part is necessary since it doesn’t do anything to the database, but oh well) All we’re doing is setting up the tables to look like the image at the beginning of the post. Then Prisma/GraphQL figures out how to connect them together in the resolvers & schema.

Now let’s regenerate our database to match our new requirements:

$ npx prisma migrate save --name "list-todo-relation" --experimental
$ npx prisma migrate up --experimental
$ npx prisma generate

Once again, we need to edit our schema.graphql and our resolvers. Let’s start with the schema.graphql:

type Mutation {
  //...
  createTodo(
    name: String!
    listId: ID!
    isCompleted: Boolean!
    order: Int
  ): Todo!
}

type List {
  id: ID!
  name: String!
  todos: [Todo!]!
}

type Todo {
  id: ID!
  name: String!
  isCompleted: Boolean!
  order: Int
  list: List!
  listId: Int!
}
  1. In our createTodo mutation you’ll notice we have a listId: ID! parameter used to identify a list.
  2. In our type List you’ll see todos: [Todo!]! which allows us to return a list of todos along with our List record when we query for a List.
  3. In our type Todo you’ll see we have list: List! and listId: Int! These allow us to access the list and listId that the todo belongs to.

By having our code setup like above, we can go both ways. We can get to the List from the Todo, and we can get to the Todos from the list.

Now we need to update our resolvers in our index.js:

const resolvers = {
  //...
  Mutation: {
    createTodo: (parent, args, context, info) => {
      return context.prisma.todo.create({
        data: {
          name: args.name,
          isCompleted: args.isCompleted,
          order: args.order,
          list: { connect: { id: parseInt(args.listId) } },
        },
      });
    }, // end createTodo...
    //...
  },
}

In the code above, our createTodo mutation includes the line list: { connect: { id: parseInt(args.listId) } }, which connects the List record to the Todo based on the args.listId passed in.

So if we pass in listId 3 with that mutation, then the created Todo belongs to list #3.

We have an error, but let’s try it out.

Save everything. Restart your server. in graphql playground, run this command and swap out my listId with your own existing list:

mutation {
  createTodo(name: "Cheese Whiz", listId: 3, isCompleted: false) {
    id
    name
  }
}

It worked! But let’s try to query for our todos AND get the list the todo belongs to:

query {
  todos {
    id
    name
    list {
      id
      name
    }
  }
}

We should be able to get the entire List record that owns each todo, but we get an error:

Each field in the “todo” record that we query (id, name, list, etc…) has a resolver. With basic queries prisma is smart enough to figure out where the fields are, but for an advanced setup like one-to-many relationships, Prisma needs a little help finding the list for that todo. We’d have the same problem finding each todo that belongs to a list… So to fix that we need to tell GraphQL how to find the List (in the Todo query) and how to find the Todos (in the List query). We do that by adding “field level resolvers” which give GraphQL instructions on how to find those relation fields.

add the following code to your resolver object in index.js

User: {
  //...
},

Todo: {
  list: (parent, args, context) => {
    return context.prisma.list.findOne({
      where: { id: parent.listId },
    });
  },
},
List: {
  todos: (parent, args, context) => {
    return context.prisma.todo.findMany({
      where: { listId: parent.id },
    });
  },
},

This code says “When the ‘list’ field is queried on the Todo object… here’s how/where you find the list…. find ONE list where the id of the list is equal to the argument listId passed in.”

in the List section it says the same thing only switched. Now save and restart the server, and try the playground query again:

query {
  todos {
    id
    name
    list {
      id
      name
    }
  }
}

Finally, make sure the lists query works:

query {
  lists {
    id
    name
    todos {
      id
      name
    }
  }
}

and you should see your results are working! You can even do silly things like this:

query {
  lists {
    id
    name
    todos {
      id
      name
      list {
        id
        name
        todos {
          id
          name
        }
      }
    }
  }
}

That’s it for One-to-many relationships in GraphQL and Prisma! In the next post we’ll cover many-to-many.

The final code is here in the done-one-to-many branch

Leave a Comment

Your email address will not be published. Required fields are marked *

want more details?

Fill in your details and we'll be in touch

%d bloggers like this: