DEV Community

Angela Lam
Angela Lam

Posted on

Self-Referential Association with Rails and React

Imagine an application similar to LinkedIn, minus the job postings and resume profiles, with a focus on the tech field. My react-redux project was aiming for something like that, but probably isn't what you imagined. With this being my first react focused application, I wanted to focus on the basics, but also challenge me to do something new. I've never implemented a following or friending component to any of my applications before. This was a new skill for me to learn using a Rails API for my backend and React-Redux for my frontend, and I want to share the information for those who want to implement a following component in their application.

First thing is creating a User model/table that with the attributes desired. We want an instance of User to be able to follow another instance of User. How we will achieve that is making a JOIN model. For my application, I called the JOIN model "Connection," which has a follower_id and a followed_id to join two users IDs. This is self-referential association, which links another model, in this case User to itself. This allows a user to follow many users and can be followed by many users. Here's how to code will look like in each of the models' class.

#user.rb
class User < ApplicationRecord
    has_many :followed_users, foreign_key: :follower_id , class_name: "Connection"
    has_many :followed, through: :followed_users

    has_many :following_user, foreign_key: :followed_id, class_name: "Connection"
    has_many :followers, through: :following_user
end
Enter fullscreen mode Exit fullscreen mode

In the first line, we’re saying that a user can have many followed_users, meaning that they can follow as many people as they want. We’re going to use the foreign key follower_id to represent these followed_users and this can be found in the Connection class. The second line of code is exactly what you’d expect to see in any has_many_through relationship. We’re saying that we have many followed through the followed_users that we’ve defined above.

#connection.rb
class Connection < ApplicationRecord
    belongs_to :follower, class_name: "User"
    belongs_to :followed, class_name: "User"
end
Enter fullscreen mode Exit fullscreen mode

The JOIN model belongs to a follower and a followed; those are a type of User, which is why we need to use class_name to state that they are from the class User.

After getting the associations working on the backend, I had to think of a way to get all Users into my redux store in my front end with all the associations. I didn't want to make a fetch request to both my Users AND my Connections. Fetches are asynchronous and not very optimal to make multiple fetch requests. If I checked my rails server in http://127.0.0.1:3000/users, I would find the following User object:

{
    "id": 1,
    "firstName": "Angela",
    "lastName": "Lam",
    "email": "angela@email.com"
}
Enter fullscreen mode Exit fullscreen mode

Of course I want the User object, therefore I would make a fetch request to that URL and get all my users. How about my connections, which tell me the follower/following between User instances? I could create a fetch request to http://127.0.0.1:3000/connections and get something like this:

{
    "id": 1,
    "followed_id": 1,
    "follower_id": 5,
 }
Enter fullscreen mode Exit fullscreen mode

But now I would have to find the Users that match the IDs in the Connection instance. Thankfully, Rails allows us to easily include related models in a controller action.

class UsersController < ApplicationController
  # GET /users
  def index
    users = User.all
    render json: users, include: [:followers, :followed]
  end
end
Enter fullscreen mode Exit fullscreen mode

In the index action, the line where I render json of User.all, I use the include option to indicate that I want to nest followers and followed. This produces a JSON like so:

{
    "id": 5,
    "firstName": "Anna",
    "lastName": "Kim",
    "email": "anna@email.com"
    "followers": [
      {
        "id": 2,
        "firstName": "Bob",
        "lastName": "Smith",
        "email": "bob@email.com"
      },
      {
        "id": 1,
        "firstName": "Angela",
        "lastName": "Lam",
        "email": "angela@email.com"
      }
    ],
    "followed": [
      {
        "id": 1,
        "firstName": "Angela",
        "lastName": "Lam",
        "email": "angela@email.com"
      }
    ]
  }
Enter fullscreen mode Exit fullscreen mode

All attributes of included objects will be listed by default and the related models are rendered and nested in the JSON data. In the JSON data above, we can see that Anna follows Angela and Anna is followed by Angela and Bob. Now on my frontend, I can fetch this JSON data and easily reference the users following a particular User and the users that the particular User follows. This is one less fetch request to perform for my application, and I get to keep associations together in one object. I can simply userObj.followers and userObj.followed to get the arrays of all the Users whom follow or are followed by the userObj.

Hopefully this article helps anyone who is trying to work with React or JavaScript with a Rails API. This method will let you implement a following/follower component to your app and allow your User model to have self-referential association, allowing Users to have many relationships between other Users. The include option works great with a simple User model, but if you have complicated attributes that you want to include/exclude, you should take a look at Serializers, which is something I might cover in the future, but not today.

Top comments (0)