В предыдущей статье мы разворачивали Graphcool Prisma с нуля. На выходе кроме всего прочего мы получили веб-интерфейс, через который можно было добавлять новые топики и публиковать их.
Но это была простейшая система без каких-либо пользователей. База данных содержала одну основную таблицу - Post (топики)
Сегодня мы добавим пользователей, а так же связь Пользователь-Топики и проверку на владельца записей, чтобы публиковать черновики могли только их владельцы.
Добавим модель пользователя.
Прежде чем связать добавить связь топик-пользователь, просто добавим самостоятельную модель User (Пользователь). Для этого откроем файл server/database/datamodel.graphql
Сейчас там прописана только модель Post (Топик).
Допишем ниже:
Схема описана, теперь надо ее задеплоить, чтобы призма обновила структуру базы данных и сгенерировала необходимые методы для API и работы с базой данных. Для этого перейдем в папку server/ и выполним prisma deploy
Результат выполнения:
Теперь у нас в базе данных появилась таблица User
Запустим веб-консоль, чтобы посмотреть какие методы API у нас теперь имеются.
Теперь у нас есть есть схема User. Но по-прежнему нет ни запросов (Queries), ни мутаций (Mutations).
query - это обычный запрос, то есть на получение данных. mutation - это запрос на изменение данных. На самом деле это все очень условно, так как GraphQL особо ничего не знает о выполняемых эти запросы/мутации резолверах (функции, обрабатывающие запросы), резолверы на query могут выполнять запросы на обновления, а мутации просто выборки данных. Но есть одна важная деталь: query за один раз можно выполнить сразу несколько и параллельно, в то время как mutation выполняется только поштучно и последовательно.
В данном случае у нас получается, что модель есть, но мы не можем с ней ничего делать, то есть через API мы не можем слать запросы ни на создание пользователей, ни на получение их списков, ничего.
Откроем файл server/src/schema.graphql и допишем в нем в Query users: [User!]! и в Mutation createUser(name: String!, email: String!, password: String!): User
итого получится
Обратите внимание на # import Post from "./generated/prisma.graphql"
Это не комментарий, это так прописана подгрузка типов нашего приложения. Редактировть тот файл нельзя, он генерируется призмой при деплое.
Перезапустим сервер приложения, чтобы вступила в силу новая схема (нажмем Ctrl+C и опять выполним yarn start) и обновим страницу.
Вот теперь у нас появились новые методы в схеме
Попробуем выполнить запрос на создание пользователя. Запрос выполняется, но результат пустой.
Это происходит потому что хотя у нас схема описана, не прописан резолвер на обработку этого запроса. То есть граф обрабатывает запрос, схема вся валидная, выполнение разрешено, но данных нет и не прописана функция на обработку запроса (возврат данных). В таком случае он просто возвращает пустое значение. А вот если бы мы прописали в Query users: [User!]! вместо users: [User!], то тут бы мы получили ошибку от графа, так как знак ! сигнализирует о запрете нулевого значения, то есть обязан быть ненулевой список, и как следует из указания User!, список этот не должен содержать нулевые значения пользователей.
Здесь на всякий случай еще раз объясню структуру запроса
mutation, логично, указывает на то, что это именно запрос из мутаций выполняется, а не просто query. Это в графе разные группы запросов.
createUser - это название конкретной операции, мы так назвали ее в схеме выше.
Все что в круглых скобках - это передаваемые параметры в запрос.
В фигурных - структура возвращаемых данных. То есть в результате выполнения, если будет создан пользователь, мы сразу получим в ответ указанные поля из данных этого пользователя.
Итак, допишем мутацию на создание пользователя. Для этого открываем файл server/src/index.js и в Mutation дописываем наш обработчик createUser.
bcrypt я подключил в этом файле выше через const bcrypt = require('bcryptjs')
Так как в этой версии приложения пакет bcryptjs не был установлен, устанавливаем его через команду yarn add bcryptjs и после этого опять запускаем сервер.
Вот теперь пользователь был создан и на выходе мы получили пароль не в чистом виде, как его передавали, а сразу его хеш.
Остается только дописать запрос на получение пользователей в Query.
Перезапускаем сервер, выполняем запрос и видим результат.
Обратите внимание, что мы не писали никаких запросов на непосредственную работу с базой данных. За нас все необходимые запросы создала призма при деплое новой схемы (когда выполняли prisma deploy). Напомню, что запросы эти пишутся в файл server/src/generated/prisma.graphql
А если еще более правильно выражаться, то там не запросы, а API-схемы для еще более низкого слоя всей этой системы - API-сервера призмы, что крутится на порту 4466. То есть получается, что наш проект крутится в своей папке, для него есть своя схема, через которую запросы транслируются на сервер призмы, которая в свою очередь работает с базой данных. При этом в призму мы деплоим изменения только в типах объектов схемы, запросы мы туда не деплоим, это уже наш локальный вопрос.
Авторизация пользователей.
Ну а теперь добавим непосредственно авторизацию пользователей. Зачем нам пользователи без этого?
В схему в Mutation допишем login(email: String!, password: String!): User
и там же ниже допишем еще одну модель.
Это чтобы в ответ мы получали не только объект пользователя, но и токен.
И допишем в резолверы.
Перезапускаем сервер, выполняем запрос на авторизацию и получаем ошибку.
Это потому что для работы авторизации требуется объявление произвольного секретного ключа.
Остановим сервер и запустим вот так: APP_SECRET="wefewfwefwef" yarn start
Вот теперь авторизация прошла успешно и мы получили не только объект пользователя, но и токен:
Запрос на получение текущего пользователя.
Теперь мы напишем такой запрос, который будет возвращать объект текущего пользователя в случае его идентификации.
Допишем в схему Query
me: User
И резолвер
Теперь токен, полученный при авторизации укажем в заголовок запроса Authorization. Если все ОК, мы получим пользователя.
Обратите внимание на приставку Bearer, ее необходимо указывать.
Вот теперь у нас есть не только создание пользователей, но и авторизация.
Связываем пользователей и топики.
Ну и последний штрих: настроим связи Топик-Пользователь и научимся получать топики конкретных пользователей и авторов топиков. Для этого нам надо подправить наши схемы пользователя и топика.
Допишем в модель Post author: User @relation(name: "UserPosts"), а в User posts: [Post!]! @relation(name: "UserPosts"). Получится
Задеплоим нашу новую схему prisma deploy.
Что примечательно, призма не просто создала новую таблицу для хранения записей Топик-Пользователь, но даже настроила первичные-вторичные ключи.
Перезапустим сервер, обновим страницу и у нас уже есть возможность прописывать в запрос пользователя получение топиков.
Сейчас у нас список пустой, потому что мы не создавали еще топики от имени пользователя.
Для удобства получение ID текущего пользователя вынесем в отдельный метод.
И с его использованием чуть перепишем мутацию создания топика.
Здесь мы просто дописали получение ID текущего пользователя и если был получен, то передаем в запрос создания топика объект с ID этого пользователя. При чем я специально оставил возможность передачи пустого объекта пользователя, чтобы оставить возможность публикации топиков и анонимно.
Вот теперь при создании топика, если пользователь авторизован, прописывается автор в топик.
А в списке топиков теперь выводятся авторы, если указаны.
А в списке пользователей видны теперь топики пользователей.
И совсем не сложно теперь в списке топиков у авторов получить все топики этих авторов.
Я уж не буду говорить, что сейчас доступны методы и на редактирование/удаление этих сущностей.
Вот так вот за вечер мы настроили себе платформу для регистрации/авторизации и публикации топиков, почти с нуля. На мой взгляд - очень неплохой результат.
Я не буду сейчас расписывать программирование фронта под все это (чтобы веб-морда для авторизации была и т.п.), это будет в следующем уроке. Скажу только что материал освоен и там не менее интересно, чем этот урок.
Исходники проекта лежат под тегом Lesson2: https://github.com/MODX-Club/prismagraphql-demo/tree/Lesson2