Итак, вы, вероятно, думаете - это просто очередное учебное пособие по GraphQL, которое просто скажет много громких слов, но на самом деле не поможет мне что-то реализовать?

✋ Нет.

Прочитав множество руководств по созданию сервера GraphQL, я понял, что ни одно из них не привело меня полностью туда, где я хотел быть. Какое разочарование.

На то, чтобы приступить к работе, у меня ушло намного больше времени, чем я ожидал.

Таким образом, я посвящен, чтобы дать вам учебное пособие, которое на самом деле выходит за рамки основ и дает некоторое представление о том, как реализовать сервер в реальном мире. Таким образом, каждый сможет насладиться поистине прекрасным ощущением от использования GraphQL.

🤞 Как узнать, что это законно?

Вот рабочая версия всего кода, описанного в этом руководстве. Идите, клонируйте его и попробуйте. Я также добавлю еще одну ссылку на репозиторий внизу этого руководства. Не стесняйтесь делать запросы на вытягивание или отмечать репо, чтобы мы могли сделать его как можно лучше!

🚧 Примечание.

GraphQL очень гибкий. Его можно реализовать миллионами разных способов - отсюда и столько путаницы. У каждого свое мнение и свой метод создания приложений. Это мое. Если у вас есть конструктивный отзыв, который я могу использовать для улучшения этого урока - поделитесь!

Хорошо, давай сделаем это!

Немного контекста 💁‍

Прежде чем я начну, вероятно, будет хорошей идеей дать некоторый контекст для людей, которые еще не знают. GraphQL был создан в 2012 году Facebook (еще раз спасибо). Он был разработан как альтернатива существующему стандарту REST для структурирования запросов к серверу.

🤔 Что такое REST?

Это то, что вы получаете, когда ложитесь спать ... Попался

Честно говоря, я хочу, чтобы эта статья была как можно более лаконичной. Чтобы помочь объяснить, вот полезная ссылка на статью, в которой объясняется концепция REST. Причина, по которой Facebook создал GraphQL в качестве альтернативы, заключалась в том, что стандарт REST имел несколько ключевых проблем:

  1. Для получения сложных объектов требуется несколько обращений к серверу - с задержкой.
  2. Вы получаете больше, чем просите. REST обычно определяет форму данных на сервере. Таким образом, вы получаете кучу данных, которые даже не используете.
  3. Чтобы понять, какую именно информацию вы получаете обратно с сервера, требуется много работы - не очень предсказуемо.

В то время у Facebook была куча увлеченных разработчиков, которые любили тестировать новые крутые концепции. Так что они начали работать над новой концепцией, которая позже стала GraphQL. Они хотели запросить у своих серверов именно то, что им нужно, и знать, что получат именно это обратно. Без пуха. 👊

Итак ... они создали новый язык, который был разработан специально для серверных запросов. Вот почему GraphQL называют языком запросов для вашего API.

Выше приведен пример запроса GraphQL, а также пример ответа JSON. Я бы описал, что происходит ... но это не требует пояснений.

Я не собираюсь вдаваться в подробности что такое GraphQL, потому что на эту тему есть много отличных статей. Однако, если вам нужна дополнительная информация, вот замечательная статья, которая дает вам отличный обзор концепций и основ GraphQL.

Давайте двигаться дальше!

Дай мне код! 👨‍💻

Ладно, ладно ... Да ну, ты требовал ... Я перехожу к коду. Но прежде чем мы начнем, нам нужно будет создать новый репозиторий Node.js и установить несколько зависимостей NPM.

🔥 Горячий совет: обратите внимание на Parcel.js, где вы найдете потрясающий сборщик приложений, который поможет вам отсортировать среду разработки за секунды (обязательно установите вашу цель на среду node). Посылка используется CodeSandbox.

PS: Я предполагаю, что вы уже знаете, как создать репозиторий Node.js. Если вы этого не сделаете, то концепции, изложенные в этой статье, могут показаться вам немного сложными. Вы все еще можете следовать, чтобы получить общее представление.

Наши зависимости NPM:

  1. Аполло-сервер
  2. "мангуста"
  3. Графические инструменты

Погодите… 🤔 кто такой Аполлон и зачем нам его сервер?

Чтобы было ясно, Аполлон - не человек. Это группа передовых разработчиков, которые делают огромные успехи в области GraphQL. Они создали набор готовых к работе инструментов и кода, которые сделают нашу жизнь очень простой, чтобы начать настройку наших серверов GraphQL.

Замечательно, теперь, когда наши зависимости установлены, давайте начнем с создания индексного файла в качестве точки входа в наше приложение.

📋 Файл: src/index.ts

Я добавил в код несколько комментариев, которые помогут объяснить, что происходит в файле. По сути, мы создали сервер и предоставили ему схему, которая содержит «пустой» тип для наших запросов (type Query) и «пустой» тип для наших мутаций (type Mutation).

  • Пусто означает, что у него нет свойств (пока).

Как я уже сказал ранее, я хочу, чтобы эта статья была как можно более лаконичной. Я предполагаю, что вы немного знакомы с тем, как писать базовые схемы GraphQL. Но если вы не знаете, вот ссылка на то, как работают базовые схемы.

Далее мы собираемся настроить таблицу базы данных для наших пользователей с помощью mongoose. У наших пользователей будет несколько основных свойств, которые мы можем использовать для запроса позже.

📋 Файл: src/common/users/user.model.ts

В приведенном выше файле мы создаем модель пользователя с помощью мангуста. Если вы не знакомы с мангустом, это элегантная оболочка, которую вы можете использовать для проверки ваших данных, когда они попадают в вашу базу данных. Это также дает нам потрясающие возможности, такие как виртуальные свойства, простой запрос данных и многое другое.

Итак, теперь у нас есть модель, которую мы можем использовать для сохранения и запроса данных из нашей базы данных, а также сервер, на котором работает пустой сервер GraphQL. Все, что нам нужно сделать, это соединить их вместе!

Для этого мы создадим файл, который будет содержать 2 вещи:

  1. Набор типов GraphQL, который сообщает клиенту, «какие» данные у нас есть.
  2. Соответствующий набор функций преобразователя GraphQL, который сообщает серверу, «как» делать то, что описывают наши типы.

Я храню эти две части кода в одном файле, потому что это облегчало мне жизнь, когда я работал.

📋 Файл: src/common/users/user.schema.ts

Святая моли! Это очень много ... Итак, давайте разберемся с этим, начав с наших определений типов:

  • type User { ... }: это простой тип GraphQL. Это просто говорит нам, какова форма пользователя, чтобы клиент мог правильно запросить его. Вы можете найти больше здесь, в документации.
  • input UserFilterInput { ... }: аналогично объекту «тип», входные данные определяют структуру сложного параметра, то есть чего-то более сложного, чем String, ID, Int, FLoat или Boolean.
  • extend type Query { ... }: помните, когда мы создавали корневой тип запроса обратно в индексном файле? Ну, это относится к тому. Мы расширяем этот корневой запрос и определяем функции, которые хотим предоставить нашему клиенту. Почему мы так поступаем? Пффф ... Не то чтобы я хотел сделать это так (это вроде хакерства) ... К сожалению, это был лучший способ сделать это из множества плохие альтернативы. Не стесняйтесь дать мне лучшее предложение.
  • extend type Mutation { ... }: так же, как мы расширяем корневой запрос, мы также расширяем корневую мутацию.

Теперь давайте разберемся, что происходит в наших распознавателях пользователей:

  • Имена наших функций распознавателя совпадают с именами полей в Query и Mutation в определениях типов. Это помогает Apollo узнать, какие функции и что делают.
  • users: async (_, { filter = {} }) => { ... }: Ну, разве эта строчка не утомляет разработчиков, которые раньше ее не видели? Не волнуйтесь, это всего лишь заявление о том, что для свойства users мы назначаем анонимную функцию, которая использует async / await для запроса к базе данных и возврата некоторых пользователей. Просто 😉. Аргументы в функции соответствуют аргументам в документации сервера Apollo, которую вы можете найти здесь.
  • await User.something(): В этом синтаксисе мы используем мангуста для получения или сохранения данных в базе данных. Это очень просто, как только вы разберетесь, вы можете найти документацию по мангусту здесь.
  • user.toObject(): Причина, по которой нам нужна эта функция, заключается в том, что Mongoose не возвращает простой объект JavaScript. Скорее, он возвращает сложный объект с некоторыми случайными свойствами, которые не нравятся GraphQL. Таким образом, используя метод toObject, мы преобразуем сложный объект в простой объект, который GraphQL может обрабатывать. Примечание: в файле модели нам нужно убедиться, что для getters и virtuals установлено значение true, чтобы убедиться, что он работает правильно.

🤯 Ничего себе! Это была перегрузка мозга.

Не волнуйтесь, если вы не сразу поймете весь код. Вы сможете клонировать и экспериментировать с репозиторием примеров на свой компьютер, как только закончите обучение.

Хорошо! Теперь давайте все вместе исправим обратно в индексный файл ...

📋 Файл: src/index.ts

Все, что нам нужно было сделать, это импортировать наши определения типов и преобразователи, а затем мы добавили их в нашу схему. Если вы пойдете и запустите свое приложение (надеюсь, вы бы настроили стартовый скрипт, т.е. npm start), вы должны увидеть, что ваше приложение откроется на http: // localhost: 4000.

Устранение неполадок: не забудьте установить и запустить базу данных MongoDB. Вот ссылка на статью, в которой показано, как это сделать, если вы еще этого не сделали.

Когда мы перейдем на сервер в нашем браузере, мы увидим, что Apollo предоставил нам небольшой полезный инструмент, называемый игровой площадкой. Мы можем использовать его для тестирования нашего сервера GraphQL. Ниже приведен пример нескольких запросов, которые я тестировал в нашем API.

Вам может быть интересно; что означает query GetAllUsers или mutation AddUser?

Не волнуйтесь, это поможет вам отладить приложение. Это просто имена, по которым вы можете идентифицировать свои запросы GraphQL. Они не добавляют дополнительных функций к запросу или изменению. Вы можете найти больше информации о том, как писать запросы и изменения здесь.

🙋‍ Привет, Джек, я все еще не уверен в одном. В чем разница между запросами и мутациями?

Отличный вопрос! У меня было предчувствие, что вы спросите. Чтобы по-настоящему понять это, нам нужно посмотреть, что происходит под капотом нашего сервера. Многие считают, что запросы эквивалентны запросу GET. Мутации относятся ко всем остальным, то есть POST, PUT, PATCH и DELETE, но это не совсем так.

Давайте посмотрим на пример двух запросов к нашему серверу GraphQL с нашей игровой площадки Apollo GraphQL, которая поставляется с Apollo Server прямо из коробки.

Как видите, запросы query и mutation являются запросами POST. Причина этого в том, что они оба имеют возможность передавать переменные в свои запросы, например. users (limit: $maxUsers) { ... }.

Настоящая разница между ними в том, что:

  1. Запросы выполняются параллельно.
  2. Мутации выполняются последовательно.

Таким образом запросы могут выполняться быстро, а изменения могут выполняться надежно. Спасибо Prisma за помощь.

⏰ Пора поднять уровень!

Итак, мы проделали достаточно хорошую работу, мы знаем, как:

  • ✅ Создайте базовый сервер.
  • ✅ Создайте схему мангуста, которая проверяет данные нашей базы данных.
  • ✅ Определите нашу структуру данных GraphQL на сервере, используя определения типов.
  • ✅ Подключите нашу схему мангуста к серверу GraphQL с помощью преобразователей.
  • ✅ Задавайте вопросы и изменяйте игровую площадку Apollo.

Я бы сказал, что это честная встряска бутылки соуса - Кевин '07. Но кое-чего еще не хватает ...

😳 Что делать, если у нас есть связанные элементы базы данных?

На самом деле это довольно просто, давайте сделаем это!

Во-первых, мы представим, что каждый пользователь связан / привязан к одной рабочей области. В этой ситуации мы можем запросить информацию из рабочей области, относящуюся к пользователю, в то же время, когда мы запрашиваем информацию об этом пользователе.

Для этого мы сначала должны определить новую модель мангуста. Мы будем использовать его для сохранения и запроса рабочих пространств из базы данных.

📋 Файл: src/common/workspace/workspace.model.ts

Подобно тому, как мы создавали наших пользователей, мы также создадим файл схемы для наших рабочих областей, чтобы их можно было запрашивать самостоятельно.

📋 Файл: src/common/workspace/workspace.schema.ts

Отлично, теперь нам просто нужно обновить индексный файл, чтобы он распознал схему GraphQL и преобразователи нашей рабочей области. Примечание: чтобы объединить преобразователи, нам нужно использовать функцию lodash merge, которая глубоко объединяет два объекта вместе.

📋 Файл: src/index.ts

После того, как вы внедрите приведенный выше код, вы сможете создавать и запрашивать рабочие области так же, как мы это делали с нашими пользователями! Но это не намного круче, чем раньше. Что будет действительно круто, так это когда мы запросим данные о рабочей области «через» пользовательский объект.

Для этого мы можем использовать классную функцию мангуста, которая позволяет нам ссылаться на элементы базы данных друг на друга (например, на рабочую область для пользователя). Эти ссылки хранятся как специальные ObjectId типы. Идите вперед и обновите нашу модель User, чтобы она могла сохранять идентификатор рабочей области для наших пользователей.

📋 Файл: src/common/user/user.model.ts

Наконец, нам нужно обновить файл схемы нашего пользователя, чтобы Apollo знал, как разрешить нашу (вложенную) ссылку на рабочую область пользователя.

📋 Файл: src/common/user/user.schema.ts

Давайте посмотрим на 2 основных изменения, которые мы только что внесли в файл схемы пользователя:

  1. type User теперь имеет 2 дополнительных свойства: workspaceId (что соответствует модели Mongoose) и workspace (в которое мы помещаем объект рабочей области, когда будем запрашивать его).
  2. Теперь в наших преобразователях есть свойство с именем User. Это одна из моих любимых частей GraphQL, поскольку она позволяет разрешать отдельные свойства типа. В приведенном выше примере мы разрешаем свойство workspace, беря workspaceId пользователя и затем используя Mongoose для его извлечения из базы данных. Это то же самое, что мы делали для обычных преобразователей запросов, но на этот раз это вложенный объект.

Теперь мы можем вернуться на нашу игровую площадку и поиграться с созданием и опросом пользователей и рабочего пространства вместе.

Черт возьми! У нас есть все необходимое, чтобы вы могли превратить его в полностью рабочий сервер.

🎉 Holy moly! Вы готовы к работе с GraphQL!

Авторизация 🕵️‍

Итак, в настоящее время у нас есть довольно хороший GraphQL API. Но есть проблема: нет ограничений на доступ к нашим данным! Чтобы это исправить, нам нужно добавить аутентификацию и авторизацию.

✋ Подождите… аутентификация и авторизация - это разные вещи?

Это распространенное заблуждение, но его важно понять, поскольку оно поможет вам создавать лучшие API:

  • Аутентификация - это идентификация человека, запрашивающего информацию, т.е. определение того, какой пользователь отправляет запрос к API.
  • Авторизация относится к разрешениям, доступным этому инициатору запроса, то есть к тем ролям, которые имеет пользователь, и достаточно ли этой роли, чтобы разрешить запрос.

🧐 Итак, как нам это реализовать?

Еще один отличный вопрос, вы действительно любопытный человек! Что ж, к сожалению, есть много способов сделать это в зависимости от того, как вы хотите, чтобы ваше приложение работало. Например:

  • Возможно, вы захотите, чтобы пользователи регистрировались только с помощью GitHub auth, а не с помощью электронной почты и пароля.
  • У вас может быть 3 разные роли пользователя, а не 100 детализированных пользовательских ролей.
  • Пользователей может не быть вовсе, все приложение можно использовать анонимно.

В любом случае, как вы реализуете аутентификацию и авторизацию, зависит от вас. Но если вы хотите получить руководство о том, с чего начать, вот ссылка на замечательную статью Prisma, которая поможет вам приступить к добавлению аутентификации в ваш API.

Чтобы упростить задачу, я также добавил в наш демонстрационный репозиторий базовую аутентификацию, которую вы можете просмотреть и проверить. Не стесняйтесь улучшить репозиторий с помощью лучшего примера аутентификации и отправить запрос на перенос!

~ Вот ссылка на демонстрационный репозиторий GraphQL ~

👏 Черт возьми! Нам удалось сделать GraphQL-сервер! Вперед!

Если вам понравилась эта статья, пожалуйста, дайте ей несколько аплодисментов (вы можете оставить до 50) или вы можете прокомментировать, если у вас есть какие-либо вопросы, я сделаю все возможное отвечать! 🙌

Подпишись на меня в Твиттере".

Спасибо!

Статья написана создателями Authenticator - простой и быстрой аутентификации для вашего приложения.

Еще сообщения Джека Скотта.