Создайте внутренний сервер

Это серия из двух частей. В первой части мы узнаем, что такое GraphQL и каковы его преимущества. Мы построим серверную часть с помощью GraphQL.

Во второй части мы научимся интегрировать серверную часть GraphQL с нашей интерфейсной службой React с помощью Apollo Client.

GraphQL существует уже довольно давно, и мы часто думаем, что GraphQL - это сложная вещь, но на самом деле все GraphQL - это спецификация того, как данные будут обмениваться между сервером и клиентом через HTTP.

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

Например, в случае среднего API у нас может быть API с именем /api/allarticles/:userId, который возвращает все статьи для определенного пользователя.

Теперь этот способ создания API известен как REST API, и мы уже довольно давно создаем API, используя этот метод. До этого у нас был протокол SOAP, в котором мы использовали структуру данных XML.

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

Начиная

Теперь, после этого небольшого введения, давайте перейдем непосредственно к демонстрации. В этой демонстрации мы сосредоточимся на создании небольшого приложения React с использованием Apollo Client. Apollo Client - это клиентская библиотека GraphQL, доступная для всех основных интерфейсных фреймворков JavaScript.

Мы будем использовать Apollo Server для построения нашей серверной части. Весь код этого руководства доступен на GitHub. Итак, приступим к созданию простого приложения.

Эта демонстрация будет сосредоточена на создании простого приложения для начала работы с Apollo Client на переднем конце, с React.js и Apollo Server для создания облегченного бэкенда GraphQL.

Начнем с создания простой структуры папок. Для простоты в этом начальном руководстве у нас будет внутренняя и внешняя части внутри одной папки. Итак, приступим.

Теперь, после настройки структуры папок, мы начнем с создания нашей серверной части, а затем перейдем к созданию клиентской части React для демонстрации наших данных.

Создание серверной службы с помощью Apollo GraphQL

Теперь, когда мы закончили с исходной папкой, давайте начнем с написания кода и узнаем кое-что о Apollo Server. Давайте перейдем к нашему index.js файлу и инициализируем наш сервер с базовой минимальной конфигурацией.

Прежде чем двигаться дальше, давайте просто проанализируем 12 строк кода, которые мы написали до сих пор, и посмотрим, с чем мы работаем. Большая часть кода довольно проста, за исключением того, что мы видим что-то под названием typeDefs и resolvers. Давайте сначала узнаем, что такое typeDefs и resolvers.

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

Эта схема может иметь три корневые операции. Этими тремя операциями являются Query, Mutation и subscription. Все они имеют определенную цель.

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

Подписки зависят от использования примитива публикации и подписки для генерации событий, уведомляющих о подписке.

На этом мы закончили базовое введение в Query, Mutation и Subscription. Точно так же resolver - это, по сути, функция или метод, который разрешает значение для поля в схеме.

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

Теперь давайте перейдем к нашему примеру приложения. Лично я предпочитаю разделять мои resolvers и typeDefs, поэтому давайте создадим наши файлы для resolvers и typeDefs.

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

Итак, давайте начнем с создания нашего первого typeDefs.

Как я сказал ранее, typeDefs - это способ для клиента подключиться к нашей внутренней службе и запросить данные. Давайте посмотрим, как мы можем это определить.

В приведенном выше примере мы определили простой Query, который помогает нам извлекать некоторые данные из серверной части, и в нашем случае это sayHello, и он возвращает тип String, как определено самим sayHello запросом. Просто убедитесь, что вы назвали свой запрос так, чтобы он был самодекларативным.

Здесь наше Query имя ясно указывает на то, что он собирается делать. Поскольку мы определили наш typeDefs, мы также должны определить нашу resolver функцию для этого запроса, который фактически будет разрешать или вычислять значение.

GraphQL делает это путем сопоставления каждого typeDefs имени с каждым resolver именем функции. В нашем случае мы должны определить resolver с тем же именем. Так что давай тоже сделаем это.

Здесь мы определили нашу sayHello функцию внутри нашего Query, и она принимает определенное значение. В нашем случае hello random person. Просто убедитесь, что тип возврата вашей resolver функции и typeDefs совпадает, иначе ваши запросы вернут null.

Теперь, когда мы создали и наши typeDefs, и resolvers файлы, нам просто нужно внести небольшие изменения в наш index.js файл, и все готово. Мы должны импортировать наши resolvers и typeDefs файлы в наш index.js файл и использовать их.

Поскольку мы закончили с нашим введением, давайте создадим простой список дел, чтобы приступить к выполнению операций CRUD с использованием GraphQL.

Здесь мы не собираемся использовать базу данных, у нас будет фальшивая база данных внутри нашей серверной службы в виде объекта json, и мы сможем манипулировать им для выполнения наших операций CRUD. Итак, давайте создадим наш поддельный файл JSON.

У нас будет три мутации для обновления, создания и удаления данных внутри нашего поддельного файла JSON и один запрос для взаимодействия с нашими данными и их выборки.

Теперь давайте создадим наш первый Query для получения данных из нашей внутренней службы. Назовем это fetchTasks.

Мы определяем нашу задачу получения Query, и она имеет тип возврата Tasks. Теперь давайте напишем функцию распознавателя для нашего недавно добавленного запроса.

Наш запрос всегда будет возвращать первую задачу. Перед обновлением этого поведения давайте сначала запустим сервер.

Перейдем к http://localhost:4000/.

Когда мы переходим к http: // localhost: 4000 /, нас приветствует этот графический интерфейс. Это известно как игровая площадка GraphQL, и здесь мы можем запускать наши запросы. Давайте запустим наш первый Query.

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

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

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

  • root - результат предыдущего / родительского типа.
  • args - аргументы, предоставленные полю клиентом. Например, в нашем typeDefs у нас есть addTask(input:addTaskInput), поэтому args в этом случае будет {input:{name:"some name",completed:false}}.
  • context - объект Mutable, предоставляемый всем распознавателям. Он в основном содержит аутентификацию, состояние авторизации и все остальное, что следует учитывать при разрешении запроса. Вы получаете доступ к своему объекту request, поэтому можете применять любое промежуточное ПО и предоставлять эту информацию своим преобразователям через контекст.
  • info - Информация о поле, относящаяся к запросу. Этот аргумент используется только в сложных случаях, но он содержит информацию о состоянии выполнения запроса, включая имя поля, путь к полю от корня и многое другое.

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

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

Здесь мы видим все наши мутации и запросы. Давайте проведем несколько мутаций и запросов и посмотрим, работает ли это.

Мы закончили сборку нашего сервера с минимальными настройками.

Во второй части этой статьи мы собираемся использовать React и Apollo Client для создания нашего внешнего клиента и использовать только что созданные API.