Лучший дизайн API Rails для получения всех записей, связанных с определенным пользователем

#ruby-on-rails #rest #api-design #ruby-on-rails-6

Вопрос:

В основном это вопрос дизайна api. У меня есть API Rails, в котором есть маршруты для пользователей и маршруты для школ. Я хотел бы сделать один вызов из моего интерфейсного приложения в api с параметром идентификатора пользователя, который возвращает все школы, связанные с этим пользователем.

Каков наилучший способ сделать это? Должен ли я создать новый маршрут в пользовательском контроллере под названием user-schools? Или новый маршрут в SchoolsController называется schools-user? Или создать совершенно новый контроллер под названием пользовательский интерфейс? Спасибо за любые рекомендации!

PS: Получение записей из ActiveRecord в контроллере не является проблемой. Проблема в том, как лучше всего спроектировать этот api.

Ответ №1:

Единственный способ определить это-через вложенный маршрут:

 GET /users/:user_id/schools
 

Те же основные принципы проектирования применяются здесь для API и «классических» приложений.

Это можно определить, вложив вызовы в макрос ресурсов:

 resources :users do
  resources :schools, only: [:index]
end
 

Это приведет /users/:user_id/schools к SchoolsController#index . Пока вы можете «понюхать» для user_id парама:

 class SchoolsController
  # GET /schools
  # GET /users/1/schools
  def index
    schools = if params[:user_id].present?
      user = User.find(params[:user_id])
      user.schools 
    else
      School.all
    end
    render json: schools
  end
end
 

Более чистый дизайн заключается в использовании отдельного контроллера для вложенного контекста:

 resources :users do
  resources :schools, only: [:index], module: :users
end
 
 module Users
  class SchoolsController < ApplicationController
    # GET /users/1/schools
    def index
      user = User.find(params[:user_id])
      render json: user.schools
    end
  end
end
 

Этот контроллер выполняет только одну работу. Вы также можете назвать его UserSchoolsController , но разделение контроллеров на папки (и пространства имен) упрощает их организацию.

Комментарии:

1. Отличный ответ. Альтернативный подход, который, я думаю, стоит упомянуть (и имеет плюсы/минусы вложенности), заключается в поддержке параметра запроса в главном контроллере, который фильтрует результаты (например schools?user=123 , или schools?user_id=123 ).

2. @melcher с точки зрения спокойного дизайна параметр запроса на самом деле не подразумевает, что существует связь между двумя ресурсами, и я бы действительно рассмотрел это только в том случае, если вам нужна одна конечная точка с множеством параметров фильтрации или если параметр на самом деле не является отношением, как, например /schools?name="Zoolander" .

3. Полностью согласен с вашими соображениями, и это зависит от варианта использования и типа отношений. Мое предположение состояло в том, что пользователи были «связаны» со школой (например, HABTM), но это не было основным отношением, поэтому вложение не обязательно имело бы смысл, особенно если вам может потребоваться отфильтровать по другим отношениям.