I have a has_and_belongs_to_many
in my Rails project. The schema look like this
create_table :dogs do |t|
t.string :name
t.string :breed
t.string :dog_type
end
create_table :users_dogs, id: false do |t|
t.belongs_to :user
t.belongs_to :dog
t.index ["user_id", "dog_id"], name: "by_user_and_dog", unique: true
t.index ["user_id"], name: "index_users_dogs_on_user_id"
t.index ["dog_id"], name: "index_users_dogs_on_dog_id"
end
create_table :users do |t|
t.string :name
t.string :email
end
And my models
class User < ApplicationRecord
has_and_belongs_to_many :dogs
validate :only_one_good_dog_per_breed
validate :only_one_bad_dog_per_breed
validate :at_least_one_good_dog
end
class Dogs < ApplicationRecord
DOG_TYPES = %w[good bad].freeze
BREEDS = %w[poodle bulldog]
has_and_belongs_to_many :users
validates :name, :breed, :dog_type, presence: true
validates :breed,
inclusion: { in: BREEDS, allowed_options: BREEDS.join(", ") }
validates :dog_type,
inclusion: { in: DOG_TYPES, allowed_options: DOG_TYPES.join(", ") }
validates :name, uniqueness: { scope: [:breed, :dog_type] }
end
As you can see there are some restrictions for which types of dogs the user can have. They can only have 1 dog_type for each breed. And the name can be anything really. And they must always have at least 1 good dog.
Ok now for my question. I have an update users API route for my API clients. Currently they can send their Dog ids as part of the user params to update their dogs.
def update
if @user.update(user_params)
render json: @user, status: :ok
else
render json: { errors: @user.errors }, status: :unprocessable_entity
end
end
private
def user_params
params.require(:user).permit(
:name,
dog_ids: []
)
end
Now I realized my API clients will have to always send over all the User dog ids if they ever want to do any of the following, update one of their dog's name, add a newly created Dog object to the list of dogs the user has, or remove a dog from the list of dogs belonging to the user.
As of right now my API only allows for a creation of a Dog object. And since these dogs can have and belong to many different users I don't want to allow the update or deletion of a Dog directly since that could affect my other users that share a dog and their dog hasn't changed at all.
I'm wondering if I should continue to require my API client to always send over all the dog_ids that are going to be associated with my user. OR if I should create three actions in my User API controller. One to add a dog to the User's list of dogs. One to update a user's existing dog (i.e. associating a new dog_id to a user and removing an existing dog id that shares the new dog's breed and dog_type per my validations). And one action to remove a dog from the list of user dogs.
What is typical of these type of APIs for a has_and_belongs_to_many type of relationship?
I'm not sure what is typical of APIs for this type of relationship. A quick Google of the topic didn't turn up anything definitive.
One thing to notice, however, is this isn't managing resources in the classic CRUD sense. It's managing associations between resources.
I imagine dogs would be managed using a conventional dogs endpoint. For example to create a new dog:
POST/api/v1/dogs
PARAMS:
- name
- breed
- dog_type
Then to create or delete an association, API client would make a request to these endpoints:
POST /api/v1/users/:user_id/dogs/:id
DELETE /api/v1/users/:user_id/dogs/:id
User-related validations would be enforced on these requests.