user12212177
user12212177

Reputation:

Trying to understand when to use nested routes vs. when not

I have the following models:

class Blog < ApplicationRecord
  has_many :posts
end

class Post < ApplicationRecord
  belongs_to :blog
end

Right now I have only two routes:

I'm not sure if I should change the second one to also accept a :blog_id. The update method (on the controller) doesn't need to use the site to update a post:

class PostsController < ApplicationController
  before_action :set_site, only: [:create]
  before_action :set_post, only: [:update]

  PERMITED_POST_PARAMS = [
    :post_id,
    :title,
    :url,
    :body
  ].freeze

  def create
    @post = @site.posts.build(post_params)

    if @post.save
      render json: @post, status: :created
    else
      render json: @post.errors, status: :unprocessable_entity
    end
  end

  def update
    if @post.update(post_params)
      render json: @post
    else
      render json: @post.errors, status: :unprocessable_entity
    end
  end

  private

  def post_params
    params.require(:post).permit(*PERMITED_POST_PARAMS)
  end

  def set_post
    @post = Post.find(id)
  end

  def set_site
    @site = Site.find(site_id)
  end

  def post_id
    params[:post_id]
  end

  def id
    params[:id]
  end
end

My question is: is it ok to do what I'm doing, or it a bad practice?

Thank you.

Upvotes: 0

Views: 51

Answers (2)

arieljuod
arieljuod

Reputation: 15838

As always, it depends. It's ok to do what you want, it's also ok to have the update nested inside the other model (it's confusing that you have "blog_id" on the route and your helpers are "post_id" and "id" but you call "id" to find a post and "site_id" which is not defined (??)).

Rails route helpers will automatically create the update nested inside the site if you do something like:

resources :blogs do
  resources :posts
end

You don't really need the blog_id, but you can use that as an extra filtering measure: if you have the blog_id, you can do @post = @blog.posts.find(...) instead of @post = Post.find(...).

Is it needed? no, but it gives you context and consistency on your routes (really opinionable of course).

(Why are you permitting post_id??)

Upvotes: 0

Greg
Greg

Reputation: 6628

I'm not sure if this answer is more like an opinion, but I'd say yes - keep it simple.

To create a post you need a :blog_id so a nested resource is one way to do it. Other would be POST /post and pass the :blog_id in the POST params.

Both seem perfectly fine, and it's up to you to design your API. The biggest rule: keep it consistent.

That being said - I can't recall single API that I was using, which used the nested resources for the create/update methods. It seems awkward: if I create a post - why one attribute of it (blog_id) should go in the path, and rest should be sent as params?

On the other hand nested resources work great for the read methods: /post/1/comments instead of /comments?post_id=1

Is has more natural fell to it - access resource /post/1 and subresources as a sub path /post/1/author, /post/1/tags

But create resources directly POST /tags, post /comments with the attributes passed on as params.

Upvotes: 1

Related Questions