Mark Peterson
Mark Peterson

Reputation: 570

RESTful design of many-to-many relationship to DELETE with one request

I am creating a RESTful API to be consumed by n+ clients over HTTP.

If I have a Users <= Friendships => Friends many-to-many relationship, should I be satisfied that in order to DELETE any of a User's Friendships, I will first have to query GET friendships#index to get the ID of the friendship that I intend to DELETE, and then issue the DELETE request?

GET /friendships?user_id=123&friend_id=456
=> {id:"999",user_id:"123",friend_id:"456"}
DELETE /friendships/999
=> "Friendship deleted"

In order for a User to DELETE a Friendship, the application needs to have the Friendship "id" available, but in order for the User to know which Friend they are deleting, they need to see the Friend "name".

In other words, is there a RESTful way to request the array of Friend models and their respective array of Friendship models in the same HTTP request?

Below is my current setup:

# Models
class User < ActiveRecord::Base  
  has_many :friendships, :dependent => :destroy
  has_many :friends, :through => :friendships
  # disregard inverse for now
end

class Friendship < ActiveRecord::Base
  belongs_to :user
  belongs_to :friend, :class_name => "User"
  validates_uniqueness_of :user_id, :scope => :friend_id
end


# Tables
create_table :users do |t|
  t.string :name
  t.timestamps
end

create_table :friendships do |t|
  t.integer :user_id, :null => false
  t.integer :friend_id, :null => false
  t.integer :status, :null => false, :default => 0
  t.timestamps
end

# routes.rb
resources :friendships, :only => [:create, :destroy, :index]
resources :users do
  member do
    get :friends # index-like action returning array of friends
  end
end

... and yes, I have been searching for days :)

I'm working with Rails, but this question stands for any framework. And yes, I know that I have a million ways to "Get 'er done", but I'm looking for the RESTful way of doing this so that I can use any client side framework without having to customize the I/O too much.

Upvotes: 1

Views: 1197

Answers (1)

Adam
Adam

Reputation: 3158

I don't think you would need to issue two separate requests. I think if you were in a position where you only knew the user_id and follower_id, you could implement a finder in your Friendship model to find a friendship based on user_id and friend_id and then delete the one that is found. Though, I think if you wanted to go the pure restful manner, it would be best to pull the friendship.id serverside when you were displaying that frienship relationship. There's a really good example of how to do this in the Rails Tutorial codebase for sample_app. Here, the join model is called relationship, and there is a RelationshipsController with create and destroy actions. The form for handling the destroy action is found in /app/views/users/_unfollow.html.erb and looks like the following:

<%= form_for(current_user.relationships.find_by_followed_id(@user),
             :html => { :method => :delete },
             :remote => true) do |f| %>
  <div class="actions"><%= f.submit "Unfollow" %></div>
<% end %>

Here, the there is a has_many :relationships on the User model and it then uses the find_by_followed_id dynamic finder from ActiveRecord to return the specific relationship object to be deleted. This seems to be the most RESTful way to do it.

Upvotes: 1

Related Questions