Дизайн REST API для сущности группы

1. Дизайн REST API для сущности группы

Условие задачи:
Необходимо спроектировать REST API для сущности Группа.

Нужно предложить интерфейс для:

  • создания группы
  • получения группы или списка групп
  • изменения группы
  • удаления группы
  • добавления и удаления связей, если группа содержит сотрудников или вложенные группы

Пример интерфейса:

  • Создание группы:
POST /api/v1/groups/
  • Получение списка групп:
GET /api/v1/groups/
  • Получение одной группы:
GET /api/v1/groups/{group_id}/
  • Частичное изменение группы:
PATCH /api/v1/groups/{group_id}/
  • Полное обновление группы:
PUT /api/v1/groups/{group_id}/
  • Удаление группы:
DELETE /api/v1/groups/{group_id}/
Спойлеры к решению
Подсказки
  • Основной ресурс — groups.
  • Для создания группы используется POST /api/v1/groups/.
  • Для получения списка групп используется GET /api/v1/groups/.
  • Для получения одной группы используется GET /api/v1/groups/{group_id}/.
  • Для частичного изменения используется PATCH.
  • Для полного обновления используется PUT.
  • Для удаления используется DELETE.
  • Связи с сотрудниками и вложенными группами лучше вынести в отдельные endpoints.
  • Добавление связи удобно делать через POST.
  • Удаление связи удобно делать через DELETE.
Решение

Основной REST API для групп #

POST /api/v1/groups/

Создание группы.

Пример запроса:

{
  "name": "Backend",
  "description": "Backend development team"
}

Пример ответа:

{
  "id": 1,
  "name": "Backend",
  "description": "Backend development team"
}

Получение списка групп:

GET /api/v1/groups/

Пример ответа:

[
  {
    "id": 1,
    "name": "Backend",
    "description": "Backend development team"
  },
  {
    "id": 2,
    "name": "Python",
    "description": "Python developers"
  }
]

Получение одной группы:

GET /api/v1/groups/{group_id}/

Пример ответа:

{
  "id": 1,
  "name": "Backend",
  "description": "Backend development team",
  "employees": [
    {
      "id": 10,
      "personnel_number": "EMP-001",
      "name": "Ivan Ivanov",
      "email": "ivan@example.com"
    }
  ],
  "child_groups": [
    {
      "id": 2,
      "name": "Python"
    }
  ],
  "parent_groups": []
}

Частичное изменение группы:

PATCH /api/v1/groups/{group_id}/

Пример запроса:

{
  "description": "Backend and API development team"
}

Полное обновление группы:

PUT /api/v1/groups/{group_id}/

Пример запроса:

{
  "name": "Backend",
  "description": "Backend and API development team"
}

Удаление группы:

DELETE /api/v1/groups/{group_id}/

Пример ответа:

204 No Content

API для сотрудников внутри группы #

Добавить сотрудника в группу:

POST /api/v1/groups/{group_id}/employees/

Пример запроса:

{
  "employee_id": 10
}

Пример ответа:

{
  "group_id": 1,
  "employee_id": 10
}

Удалить сотрудника из группы:

DELETE /api/v1/groups/{group_id}/employees/{employee_id}/

Пример ответа:

204 No Content

Получить сотрудников группы:

GET /api/v1/groups/{group_id}/employees/

Пример ответа:

[
  {
    "id": 10,
    "personnel_number": "EMP-001",
    "name": "Ivan Ivanov",
    "email": "ivan@example.com"
  }
]

API для вложенных групп #

Добавить вложенную группу:

POST /api/v1/groups/{group_id}/child-groups/

Пример запроса:

{
  "child_group_id": 2
}

Это означает, что группа 2 будет добавлена внутрь группы 1.

Пример ответа:

{
  "parent_group_id": 1,
  "child_group_id": 2
}

Удалить вложенную группу:

DELETE /api/v1/groups/{group_id}/child-groups/{child_group_id}/

Пример ответа:

204 No Content

Получить вложенные группы:

GET /api/v1/groups/{group_id}/child-groups/

Пример ответа:

[
  {
    "id": 2,
    "name": "Python",
    "description": "Python developers"
  }
]

Получить родительские группы:

GET /api/v1/groups/{group_id}/parent-groups/

Пример ответа:

[
  {
    "id": 1,
    "name": "Backend",
    "description": "Backend development team"
  }
]

Итоговый список endpoints #

POST   /api/v1/groups/
GET    /api/v1/groups/
GET    /api/v1/groups/{group_id}/
PATCH  /api/v1/groups/{group_id}/
PUT    /api/v1/groups/{group_id}/
DELETE /api/v1/groups/{group_id}/

GET    /api/v1/groups/{group_id}/employees/
POST   /api/v1/groups/{group_id}/employees/
DELETE /api/v1/groups/{group_id}/employees/{employee_id}/

GET    /api/v1/groups/{group_id}/child-groups/
POST   /api/v1/groups/{group_id}/child-groups/
DELETE /api/v1/groups/{group_id}/child-groups/{child_group_id}/

GET    /api/v1/groups/{group_id}/parent-groups/

Важные проверки #

1. Нельзя создать две группы с одинаковым name, если name должен быть уникальным.
2. Нельзя добавить в группу несуществующего сотрудника.
3. Нельзя добавить несуществующую вложенную группу.
4. Нельзя добавить группу саму в себя.
5. Желательно запретить циклические связи групп.
6. Повторное добавление одной и той же связи должно либо игнорироваться, либо возвращать 409 Conflict.

Пример ошибки при попытке добавить группу саму в себя:

{
  "detail": "Group cannot contain itself"
}

Пример ошибки при повторном добавлении связи:

{
  "detail": "Relation already exists"
}

Такой интерфейс разделяет обычные CRUD-операции над группой и операции управления связями. Это делает API понятным: сама группа редактируется через /groups/{group_id}/, а связи с сотрудниками и вложенными группами управляются через отдельные вложенные ресурсы.