Taking GraphQL One Step Further with Relationships

Andrew Smoker
3 min readMay 27, 2021

--

Last week I gave a basic overview of GraphQL and how to set up Queries, Mutations and Types in a Ruby on Rails application. Today I want to take that one step further by adding relationships between the models I set up. I am continuing with the same Note Taking application, but adding the relationship that a note belongs to a user and a user has many notes. I also added another model, Category, with a join table so that notes can have many categories and a category can have many notes. Using GraphQL with these types of relationships was tough at first, but once you get the hang of it, it’s not that bad. Let’s dive in!

Belongs_to

For the belongs_to relationship we need to add the user ID onto the NoteType we created earlier by adding this line:

field :user, UserType, null: true

This gives us access to the user the note belongs to by writing a query like this:

query {
fetchNotes {
id
title
body
user {
username
email
}
}
}

Has_many

On the other end, where a user has many notes, we can update the UserType by writing this line:

field :notes, [NoteType], null: false

This gives the field “notes” that will result in an array [] of NoteTypes that belong to the user. We can then write a query like this:

query {
fetchUsers {
id
username
email
notes {
id
title
body
}
}
}

So far, the set up is fairly simple for a belongs to/has many relationship. But things get a little more complicated with a join table for a has many/has many relationship. My join table links Notes with Categories so they can each have many of the other. What I learned was how to set up another folder called Loaders and create the modules for has_many and belongs_to that you can use on any Type. First the has_many:

Has_many Module

module Loaders
class HasManyLoader < GraphQL::Batch::Loader
def initialize(model, column)
@model = model
@column = column
end
def perform(relation_ids)
records_by_relation_id = @model.where({ @column =>. relation_ids.uniq }).group_by do |result|
result.public_send(@column)
end
relation_ids.each do |id|
fulfill(id, records_by_relation_id[id])
end
end
end
end

This module sets up a has many relationship so you can call on it by passing it the model and column name. For example, with Notes/Categories I would be able to write a method in my NoteType like:

def category_notes
Loaders::HasManyLoader.for(CategoryNote, :note_id).load(object.id)
end

Passing it the model, which in this case is my join table model called CategoryNote, and the :note_id, I get back all of the joins that have the specific note_id I want. From there, the CategoryNote belongs to a Category so I can access the Category information after I set up the belongs_to loader which looks like:

Belongs_to Module

module Loaders
class BelongsToLoader < GraphQL::Batch::Loader
def initialize(model)
@model = model
end
def perform(ids)
@model.where(id: ids.uniq).each do |record|
fulfill(record.id, record)
end
ids.each do |id|
fulfill(id, nil) unless fulfilled?(id)
end
end
end
end

Then we can write our categories method in our NoteType file like this:

category_notes.then do |cn|
if cn
category_ids = cn.map(&:category_id)
Loaders::BelongsToLoader.for(Category).load_many(category_ids)
end
end

This takes the Category ids from our join table that we found through our has_many loader and returns the categories with those ids if categories exist. Then we can write our query and request all the categories of our specific note right from the beginning! The more I learn about graphQL the more I understand why it’s being used a lot. I’m excited to keep learning about this in future weeks!

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Andrew Smoker
Andrew Smoker

Written by Andrew Smoker

I am 34 years old and making a huge career change by attending Flatiron School’s Software Engineering Bootcamp. Excited to learn!

No responses yet

Write a response