Nuxt — "фреймворк над фреймворком Vue" или популярная конфигурация Vue-based приложений с использованием лучших практик разработки на Vue. Среди них: организация каталогов приложения; включение и преконфигурация самых популярных инструментов в виде Nuxt модулей; включение Vuex по-умолчанию в любую конфигурацию; готовый и преднастроенный SSR с hot-reloading'ом Django — самый популярный веб-фреймворк на почти самом популярном языке программирования на сегодняшний день — Python. Сами разработчики позиционируют проект как "Веб-фреймворк для перфекционистов с дедлайнами". Представляет из себя решение "всё в одном" и позволяет в кратчайшие сроки построить MVP вашего веб-приложения. GraphQL — язык запросов изначально созданный компанией Facebook. В статье будет говориться о конкретных реализациях протокола этого языка, а именно библиотек Apollo для фронтенда и graphene для бэкенда. В этой статье вы сможете узнать как можно собрать dev-окружение современного SPA приложения с server side рендерингом, на основе фреймворков Django и Nuxt, а также их сообщения посредством GraphQL API.  На примере простейшего приложения со списком задач, я попытался рассказать об основных проблемах с которыми я столкнулся в процессе построения приложения на выбранном стеке. Описание старался делать как можно более понятным, в том числе и новичкам в программировании (коим, буду честен, я считаю и себя), и приводить как можно больше ссылок. Исходя из того, что эту статью вы читаете на русском языке, я делаю смелое предположение, что и остальные материалы по программированию вы также предпочитаете читать на русском, поэтому все ссылки я старался подбирать в соответствии с этим предположением насколько это возможно. Искрене надеюсь, что статья поможет сэкономить хоть немного времени тем энтузиастам, кто решит создать свое приложения на базе приведенных выше технологий, а также дать быстрый старт всем интересующимся без необходимости проводить часы и дни в поисках причин возникающих проблем, а затем и их решений на просторах интернета. Построение приложения будет вестись поэтапно, чтобы на каждом этапе можно было удостовериться что всё работает правильно. Убедитесь, что у вас уже установлен node.js и интерпретатор python. В примере используются версии: 13.9 и 3.7 соответственно.  В качестве менеджера виртуального окружения python в статье будет использоваться pipenv. Консольные команды в статье запускаются в оболочке bash. Если вы пользователь Windows, то вместо команд cd, mv, mkdir используйте аналоги, и благодаря кросс-платформенной природе python и node, всё остальное должно работать вне зависимости от ОС. В качестве базы данных для простоты будет использоваться Sqlite, которая не требует дополнительной конфигурации. Версии всех пакетов вы всегда можете посмотреть в репозитории статьи. Ниже я приведу те, что мы будем устанавливать вручную. Python библиотеки Javascript библиотеки Для начала необходимо установить менеджер виртуального окружения. В примере я буду использовать pipenv. Для установки: В некоторых операционных системах для этого действия могут потребоваться права суперпользователя. Также pipenv можно установить из репозитория вашей операционной системы. Создадим директорию с проектом и инициализируем в ней окружение pipenv. В моем случае проект будет располагаться по пути ~/Documents/projects/todo-list. Создадим эту директорию и перейдем в неё. Создаем виртуальное окружение и одновременно устанавливаем django и graphene_django: Библиотека graphene_django позволяет описывать схему GraphQL API на основе моделей Django ORM. Очень удобно, но как по мне, со связыванием таблиц БД и фронтом напрямую нужно быть очень осторожным. Для начала активируем виртуальное окружение pipenv. Далее в статье будет предполагаться, что все комманды будут выполняться внутри окружения. Создаем проект Django.  Так как фронтенд и бэкенд нашего todo-листа будет находиться в одной директории, было бы неплохо иметь все управляющие файлы в корневой директории проекта. В Django управляющим файлом является manage.py, давайте вынесем его из директории backend на уровень повыше.  Для этого, из корневой директории проекта: После перемещения нужно исправить путь к файлу настроек внутри файла manage.py. Также в файле backend/backend/settings.py приведем следующие переменные к виду: В файле backend/backend/settings.py в переменную INSTALLED_APPS добавляем установленный ранее graphene_django: По-умолчанию сервер запускается на порту 8000. Переходим на http://localhost:8000/, он должен нас встречать следующей картиной:  После изменений ниже http://localhost:8000/ уже не будет встречать нас ракетой. В файле backend/backend/urls.py Создадим пустую схему, например, в файле backend/backend/api.py В файл настроек необходимо добавить переменную GRAPHENE, в которой мы укажем путь до нашей схемы: Проверяем работоспособность. Запускаем сервер уже известной командой runserver: и переходим на http://localhost:8000/graphql/. Там нас должна встретить та самая мини "IDE" GrapiQL:  Ничего страшного в том, что нас встречает ошибка. Она появляется из-за того, что наша схема пуста. Мы исправим это при реализации дальше. Создадим приложение todo_list и модели к нему. Не забывайте, что все команды должны выполняться внутри окружения pipenv: Скрипт django-admin не знает где находится корень нашего приложения, поэтому нам нужно немного подправить файл backend/todo_list/apps.py, чтобы он выглядит следующим образом: Добавим наше новое приложение в INSTALLED_APPS, что находится в файле settings.py: Добавим модели Todo и Category в файл backend/todo_list/models.py: Для того, чтобы наши модели превратились в таблицы в БД, нужно выполнить следующее:  Создать файлы миграций, в которых будет описываться наша текущая схема: С примерно таким выводом:
 Применить эти миграции командой migrate. Т.к. это первый запуск скрипта migrate, у нас также будут применяться миграции приложений Django:  Опишем типы, создадим запросы и мутации для наших новых моделей. Для этого в директории приложения todo_list создадим файл schema.py, со следующим содержимым: После создания классов мутации и запроса, их нужно добавить в нашу схему. Как вы, возможно, помните схему мы описывали в файле api.py: Если хотите лучше понять происходящее, можете прочитать эту статью на Хабре, или обратиться к документации Graphene (англ.). ID записей в примерах ниже могут различаться с ID ваших записей. Запускаем сервер привычной командой runserver: Идем по пути http://localhost:8000/graphql/. Там нас должен встречать уже знакомый интерфейс graphiql. И как вы, возможно, заметили ошибка пропала.  Давайте проверим получившиеся запросы и мутации. Запрос Переменные Результат
 В результате этой мутации у нас создалось две записи: Запрос Результат:
 Мутация Переменные Результат:
 Мутация Переменные можно оставить из предыдущей мутации. Результат
 Чтобы ближе познакомиться с синтаксисом и терминологией GraphQL, можете ознакомиться с этой статьей на Хабре. Откройте консоль внутри корневой директории проекта и запустите скрипт установки Nuxt: Запустится очень простой и понятный скрипт установки, который предложит указать описание проекта и предоставит на выбор для установки несколько библиотек. Можете выбрать, что захотите, но из рекомендуемых пунктов я бы посоветовал выбрать "Custom UI Framework: vuetify", т.к. в примере используется именно он, и "Rendering mode: Universal", т.к. в статье рассматривается пример именно с SSR. Пример моей конфигурации:
 На установку зависимостей может потребоваться некоторое время. После завершения работы скрипта вам предложат проверить его работоспособность. Давайте сделаем это: и перейдем на http://localhost:3000. Там нас должна ждать страница приветствия Nuxt + Vuetify:  Как я говорил ранее, фронтенд и бэкенд будут находиться у нас в одной директории, поэтому было бы неплохо перенести конфигурационные файлы и зависимости на уровень повыше. Для этого из корневой папки проекта выполните: Затем в файле nuxt.config.js указываем корневую директорию приложения: После этого желательно еще раз убедиться в работоспособности проекта, выполнив запуск dev-сервера уже из корневой директории: Любой компонент с префиксом v- это компонент UI-toolkit'a Vuetify. У этой библиотеки отличная и подробная документация. Если вы хотите подробнее узнать, что делает тот или иной компонент, смело вбивайте в гугл v-component-name. Только не забывайте, что в примере используется версия vuetify 1.5. Приводим файл frontend/layouts/default.vue к виду: Создадим компонент нового Todo по пути frontend/components/NewTodoForm.vue: Далее компонент существующего Todo, там же: И наконец вставим новые компоненты в index.vue, и добавим в него немного рыбы: После проделанной работы рекомендую проверить работоспособность получившегося макета. Запустите dev сервер и перейдите на http://localhost:3000/, там вы должны увидеть следующую картину:  В Django по-умолчанию используется CSRF защита. Эта защита реализуется при помощи промежуточного слоя (middleware) — CsrfViewMiddleware. Посмотреть на него вы можете в файле settings.py в переменной MIDDLEWARE. Принцип его работы очень прост: у любого POST-запроса к Django в заголовках должен иметься CSRF-токен. Если этот токен отсутствует, то сервер просто отклоняет этот запрос. CSRF-токен в классическом django приложении приходит вместе с любым GET-запросом, после чего при необходимости добавляется в формы при рендеринге шаблона. В нашем случае проблема в том, что вне зависимости от того, выполняется в Apollo мутация или запрос, метод их по-умолчанию всегда будет POST. Apollo позволяет изменить это поведение таким образом, чтобы для Query метод запроса был GET, а для Mutation — POST, но насколько я знаю, graphene на данный момент не поддерживает подобный режим работы.  Я поступил следующим образом: немного расширил логику стандартного CsrfViewMiddleware таким образом, чтобы он проверял тип GraphQL запроса, и уже на основе этого принимал или сбрасывал соединение. Для этого добавим кастомную проверку CSRF, например, в уже знакомый нам файл api.py Далее, в файле settings.py нужно заменить "оригинальный" CsrfViewMiddleware, на кастомный: Если уважаемый читатель знает более надежные и правильные способы CSRF-защиты в связке Django + Nuxt + Apollo, то призываю поделиться своим знанием в комментариях. Т.к. dev сервера бэкенда и фронтента у нас стоят на разных портах, то Django нужно оповестить, с каких хостов могут совершаться запросы, и какие заголовки ему разрешено обрабатывать. А поможет нам в этом библиотека django-cors-headers: В settings.py добавим:  Для Nuxt существует собственный модуль apollo, который в свою очередь основан на библиотеке vue-apollo (которая в свою очередь основана на Apollo). Для его установки введите: Также при конфигурации Apollo нам понадобится небольшая библиотека cookie-universal-nuxt для манипуляции куками при рендере на стороне сервера.
Добавим эти модули в nuxt.config.js. В зависимости от вашей изначальной конфигурации там уже может быть несоклько модулей. Как минимум там должен быть vuetify: Настройка Apollo дело невсегда тривиальное. Постараемся обойтись минимальной конфигурацией. Создадим файл по указанному выше пути: Конфигурируем клиент Apollo, а вместе с тем формируем цепочку обработчиков запросов. На этом этапе лучше еще раз удостовериться, что Nuxt собирается без ошибок, запустив dev сервер. И вот наконец настало время соединить фронт и бэк. Для начала где-нибудь создадим файл, в котором будут храниться все запросы и мутации к бэкенду. В моем случае этот файл расположился по пути frontend/graphql.js с уже знакомым нам содержимым: Теперь нужно немного изменить уже существующий функционал фронтенда. Наконец пришло время добавить туда взаимодействие с бэкендом. Изменим Vue компоненты: Проверим, что у нас получилось  В этой статье я попытался рассказать как построить взаимодействие между Django и Nuxt с помощью GraphQL API, показать решение проблем с которыми довелось столкнуться мне. Надеюсь это подтолкнет энтузиастов попробовать что-то новое, и сэкономит время в решении проблем.  Весь код доступен на GitHub. Fullstack web developer