Статья представляет собой сборник ссылок и рецептов, позволяющих настроить API более гибко, более удобно для frontend разработки. Основная часть статьи будет посвящена интеграции OpenAPI в экосистему Django/DRF. Коренная мотивация интеграции и поддержки OpenAPI в проекте - документирование API эндпоинтов и возможность для клиентов API генерировать часть кода на базе схемы OpenAPI. Существуют различные решения для генерации клиентских типов/моделей для JS/TS, например, openapi-typescript-codegen, openapi-typescript, или более общее решение для широкого спектра языков. Из коробки Django/DRF предоставляет нам не сильно богатую автоматическую генерацию схемы API, поэтому будем далее разбираться, как и с помощью чего мы можем это сделать. Для Django/DRF существуют отличные решения, одни из самых популярных - drf-yasg и drf-spectacular. drf-yasg в целом отличное решение, однако оно не поддерживает OpenAPI 3.0. Будем идти в ногу со временем, поэтому для последующих примеров будет взят drf-spectacular. Документация у библиотеки отличная, поэтому далее просто её устанавливаем и интегрируем в наш проект, затем добавляем в urls.py redoc/swagger, чтобы можно было пользоваться из UI. drf-spectacular неплохо строит документацию по обработчикам API методов - action'ов из различных View из DRF, однако, на практике зачастую этого не хватает. Большинство наших потребностей можем покрыть с помощью декоратора @extend_view. Если вкратце, то с помощью него мы можем описать параметры запроса (request), формат ответа и код статуса ответа (responses), описание, примеры использования и др. для текущего API метода. Очень удобно использовать сериализаторы в качестве описания форматов ответа/запроса. Однако, в случае кастомизации форматов запроса/ответа сервера одним декоратором мы уже не обойдёмся и за схемой OpenAPI также необходимо будет следить. Рассмотрим далее некоторые полезные практики, а также проблемы и их решения в рамках drf-spectacular. Теперь мы вооружены генератором схемы API, однако, нам необходимо быть уверенными в том, что наша схема действительно соответствует API. Для валидации схемы и проверки её соответствия текущему API можем воспользоваться библиотекой drf-openapi-tester. По сути, всё что нам необходимо - при тестировании API эндпоинтов добавить вызов который даст нам знать, всё ли у нас в порядке с генерацией OpenAPI схемы. Не всегда хочется создавать несколько сериализаторов ради того, чтобы следующий эндпоинт отдавал немного больше или немного меньше полей относительно уже имеющегося другого эндпоинта. Для решения этой проблемы есть несколько вариантов: при запросе данных принимать параметр fields с перечислением необходимых клиенту полей, или же самим, на стороне backend'а устанавливать ограничения на отдаваемые данные. Недолго думая, делаем выбор в пользу второго варианта, т.к. в случае первого варианта: 1) OpenAPI будет генерировать тип ответа со всеми полями, что не всегда необходимо, 2) Для эндпоинтов может быть настроен разный уровень доступа, а в данном случае мы можем дать доступ клиенту к данным, которые он видеть не должен. В целом, для обоих вариантов есть хорошее решение в виде библиотеки drf-flex-fields. Мне сложно будет сказать, насколько хорошо drf-flex-fields интегрируется с drf-spectacular/drf-yasg, т.к. я не стал подключать эту библиотеку и ограничился решением из документации DRF: В случае динамических полей возникает сложность при генерации схемы, т.к. теперь у нас имеются несколько сериализаторов с одинаковыми именами и по итогу на выходе мы имеем не совсем валидную схему. Для этого мы добавили строку self.ref_name = kwargs.pop('ref_name', None), позволяющую при использовании одинаковых сериализаторов с разными полями прописывать уникальное название для генерации валидной OpenAPI схемы. Однако, в случае drf-spectacular этого оказалось мало и в код необходимо добавить так называемый OpenApiSerializerExtension (в терминологии drf-spectacular): Более подробно об этой магии можно почитать в соответствующем issue.Всё готово, теперь при вызове сериализатора можем пользоваться возможностью задавать любые поля и быть спокойным за OpenAPI схему, что можем сделать подобным образом: По умолчанию DRF отдаёт ответ на запрос в виде списка полей. Иногда это не покрывает всех требований и возникает необходимость обернуть ответ, например, в body: {}, а ошибки в errors: []. Для этого можно переписать метод render() у классаrest_framework.renderers.JsonRenderer, например, так: и не забудем добавить JSONResponseRenderer в конфигурацию REST_FRAMEWORK в settings.py: В данном случае сразу же возникнет проблема с OpenAPI схемой, т.к. drf-spectacular будет по-прежнему отдавать стандартный ответ. Для решения проблемы необходимо расширить все сериализаторы из responses в декораторе @extend_schema, базовые вариант можно посмотреть в FAQ drf-spectacular, в нашем случае вариант с адаптацией под использование динамических полей может выглядеть следующим образом: В предыдущем примере кода задействован сериализатор ApiErrorSerializer для, соответственно, отдачи ошибок API в теле ответа. Выглядит он достаточно просто: Но не забываем, что код выше был необходим для генерации схемы, для реальных же ответов можем написать свой обработчик исключений, или воспользоваться библиотекой drf-standardized-errors. С ней наши ошибки будут выглядеть следующим образом: Если мы хотим кастомизировать данный ответ, например, поменять названия полей или убрать атрибут type, то можем переписать ExceptionFormatter следующим образом: И не забудем добавить его в settings.py: Не будем вдаваться в детали на какой стороне лучше менять стиль написания, просто примем за факт, что данная задача может возникнуть. Решение для DRF существует в виде библиотеки djangorestframework-camel-case. В основном всё решается конфигурированием REST_FRAMEWORK в settings.py. Описание и интеграцию библиотеки можно посмотреть здесь. Для drf-spectacular мы просто добавляем пару полей в конфигурации SPECTACULAR_SETTINGS в settings.py: А также, раз мы используем модифицированный renderer, изменим и его согласно запросу на CamelCase: Хорошая документация облегчает взаимодействие с пользователями вашего API, а в случае генерации моделей/типов на основе OpenAPI схемы может ещё и существенно ускорить разработку коллегам.