Search code examples
ruby-on-railsjsonjson-api

JSON API: How to make sure a resource belongs to current_user?


Need some help with JSON API structure.

Say we have the following:

class User < ApplicationRecord
 has_many :posts
end

class Post < ApplicationRecord
 has_many :comments
 belongs_to :user
end

class Comment < ApplicationRecord
 belongs_to :post
 has_many :talkbacks
end

class Talkbacks < ApplicationRecord
 belongs_to :comment
end

Now, the api points should be something like the following:

/posts
/posts/:id
/posts/:id/comments
/comments
/comments/:id
/comments/:id/talkbacks
/talkbacks
/talkbacks/:id

If we'd like to show a post, making sure posts belong to the current user is easy assuming we have a token:

# /posts/:id
current_user.posts.find_by_id!(params_id)

However, if we want to show a specific talkback, it is more difficult to make sure the talkback belongs to the user:

# /talkbacks/:id

What would be the best way to make sure the user can access to that talkback?


Solution

  • You should flesh out your relationships with a has_one, through relation. Then, it's easy to perform the query. You don't need to add a user_id to the task field (and shouldn't as the post should handle that association). The has_one relation allows you do effectively have a belongs_to relation through another model and removes the need to have a join table.

    class User < ApplicationRecord
      has_many :posts
      has_many :comments, through: :posts
      has_many :talkbacks, through: :comments
    end
    
    class Post < ApplicationRecord
      belongs_to :user
    
      has_many :comments
      has_many :tasks, through: :comments
    end
    
    class Comment < ApplicationRecord
      belongs_to :post
    
      has_one :user, through: :post
    
      has_many :talkbacks
    end
    
    class Talkbacks < ApplicationRecord
      belongs_to :comment
    
      has_one :user, through: :comment
    end
    

    Then you can do, in your controller,

    current_user.talkbacks.find(params[:id])
    

    As an aside in your post...

    current_user.posts.find_by_id!(params_id)
    

    posts.find_by_id!() is equivalent to posts.find() so you don't need to do the by_id! part. By default, Rails will raise an exception if it cannot find a record with the find method, same as using the bang on a find_by_id method.