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

Теперь давайте сосредоточимся на подписках GraphQL. Подписка — одна из ключевых операций, предоставляемых GraphQL. В основном они используются в приложениях реального времени для уведомления пользователей о возникновении определенного события. Чтобы ознакомиться с этой темой, подумайте о группе сообщений в используемом вами приложении для обмена сообщениями. Здесь, если кто-то отправит сообщение в группу, каждый участник немедленно получит это сообщение. Хотя это может показаться сложным, эту функциональность можно легко реализовать с помощью подписки на GraphQL. Что нам нужно, так это настроить событие, которое будет запускаться, как если бы кто-то отправил сообщение группе. А затем подпишите всех участников группы на это событие. После этого при каждом запуске события данные будут отправляться всем участникам. В дополнение к подпискам в GraphQL есть Live Queries, которые также можно использовать таким же образом.

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

Перейдем к реализации подписок GraphQL. Для этого мы используем JavaScript и Apollo Server. (сервер Apollo будет использоваться как сервер GraphQL)

Давайте начнем

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

Шаг 1. Настройте новый проект Node и установите зависимости

Во-первых, вам нужно настроить проект узла. Если вы еще не установили node, следуйте инструкциям по этой ссылке. Создайте новый каталог для проекта узла и запустите этот код.

npm init -y

Установите эти зависимости в проект.

npm i graphql
npm i apollo-server-express
npm i graphql-subscriptions
npm i subscriptions-transport-ws
npm i @graphql-tools/schema

Создайте файл index.js в каталоге. Затем перейдите к файлу package.json и измените поле scripts, как показано ниже.

“scripts”: {
“test”: “echo \”Error: no test specified\” && exit 1",
“start”: “node index.js”
},

Шаг 2: Настройте сервер

Импортируйте следующие модули в проект в верхней части файла index.js.

const { createServer } = require(“http”);
const express = require(“express”);
const { execute, subscribe } = require(“graphql”);

Добавьте асинхронную функцию и настройте новое экспресс-приложение и HTTP-сервер внутри него. Он будет использоваться для управления всеми HTTP-запросами.

(async () => {
     const app = express();
     const httpServer = createServer(app);
})();

Теперь файл index.js будет отображаться следующим образом.

Остальной код также должен быть добавлен внутрь асинхронной функции.

Шаг 3. Настройте подэкземпляр Pub

Чтобы отслеживать события, нам нужно реализовать подсистему pub. В библиотеке graphql-subscriptions существует встроенная в память подсистема публикации по умолчанию, и мы собираемся использовать ее здесь. Поскольку это система в памяти, лучше не использовать ее для производственных сред. Для этого сообщество Apollo предлагает использовать свои подбиблиотеки пабов, созданные сообществом. Я надеюсь обсудить больше о подсистемах пабов в моей следующей статье.

Вам нужно импортировать модуль в верхней части кода.

const { PubSub } = require(“graphql-subscriptions”);

Создайте новый экземпляр pub внутри асинхронной функции.

const pubsub = new PubSub();

Шаг 4: Определите схему GraphQL

Далее вам нужно определить структуру ваших данных в GraphQL. Для этого нам нужно добавить следующую схему.

const typeDefs = gql`
    type Query {
        viewMessages: [Message!]
    }
    type Mutation {
        sendMessage(name: String, content: String): Message!
    }
    type Subscription {
        receiveMessage: Message!
    }
    type Message {
         id: ID!
         name: String!
         content: String
}`;

Шаг 5: Добавьте преобразователи

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

let messages = []
const resolvers = {
    Query: {
       viewMessages() {
          return messages;
       },
    },
    Mutation: {
        sendMessage: (parent, { name, content }) => {
            const id = messages.length;
            var new_message = {
                id,
                name,
                content
            }
            messages.push(new_message);
            pubsub.publish(“MessageService”, {receiveMessage:  new_message});
            return new_message;
        },
    },
    Subscription: {
          receiveMessage: {
            subscribe:()=> pubsub.asyncIterator([“MessageService”]),
          },
     },
};

Здесь вы можете увидеть, как подмодель паба используется для подписки. Как обсуждалось ранее, для активации подписки должно быть событие. В приведенном выше коде имя этого события — «MessageService». (Вы можете выбрать для этого любое имя. Но не забудьте использовать то же имя события и в преобразователе подписки). С одной стороны, данные должны быть опубликованы для этого события, а с другой стороны, данные должны быть возвращены всем подписчикам этого события. Для этого внутри преобразователя мутаций sendMessage сообщение публикуется в подсобытии pub. А в распознавателе подписки на ReceiveMessage он возвращает AsyncIterator из подпрограммы pub (клиенты подписываются на событие внутри этой функции). Используя это, сервер подписки в JavaScript извлекает все опубликованные данные с помощью цикла и возвращает данные в виде ответов GraphQL всем подписанным клиентам.

Асинхронный итератор

AsyncIterator может работать как обычный итератор, но у него есть метод next(), который возвращает промисы. Из-за этого, когда метод next() вызывается во внутреннем цикле, упомянутом выше, он должен ждать, пока данные не поступят в AsyncIterator. Поэтому только когда данные публикуются, AsyncIterator может возвращать данные.

Шаг 6: Включите подписки

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

const { ApolloServer, gql } = require(“apollo-server-express”);
const { SubscriptionServer } =require(“subscriptions-transport-ws”);
const { makeExecutableSchema } = require(“@graphql-tools/schema”);

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

const schema = makeExecutableSchema({ typeDefs, resolvers });
const server = new ApolloServer({schema,});
await server.start();
server.applyMiddleware({ app });
SubscriptionServer.create({ schema, execute, subscribe },{ server: httpServer, path: '/graphql'});

Шаг 7: Прислушивайтесь к запросам

const PORT = 4000;
httpServer.listen(PORT, () => {
console.log(`Query endpoint ready at http://localhost:${PORT}${server.graphqlPath}`);
console.log(`Subscription endpoint ready at ws://localhost:${PORT}${server.graphqlPath}`);
});

На этом настройка сервера для асинхронной отправки/получения сообщений завершена. Окончательная версия файла index.js отображается следующим образом.

Шаг 8. Тестирование подписок

Так как мы сейчас настроили сервер, нам нужно протестировать его функциональность. Apollo также обеспечивает поддержку на стороне клиента, но здесь будет обсуждаться только сторона сервера. Вместо этого мы можем использовать Apollo Studio Explorerr (GraphQL IDE, предоставленный Apollo) для тестирования нашего сервера.

Сначала запустите проект узла

npm start

Затем вы увидите две ссылки в консоли

Конечная точка запроса готова по адресу http://localhost:4000/graphql

Конечная точка подписки готова по адресу ws://localhost:4000/graphql

Чтобы войти в Apollo Studio Explorer, вы можете либо перейти по ссылке конечной точки запроса, либо просто перейти по этой ссылке. Когда вы войдете в GraphQL IDE, в левом верхнем углу появится ссылка, и проверьте, является ли эта ссылка вашей конечной точкой HTTP.

После этого нужно добавить запрос на подписку в IDE, как на картинке ниже. Вы можете использовать этот запрос для этого.

subscription receive {
     receiveMessage{
          id
          name
          content
     }
}

Теперь нажмите кнопку с названием операции вашего запроса. Затем в правом нижнем углу вы увидите, что соединение WebSockets установлено и теперь прослушивает события.
Вы должны отправлять сообщения, чтобы вызвать событие, чтобы вы могли получать данные из подписок. Для этого вы создаете новую вкладку в поле «Операция». И в новой вкладке добавьте следующую мутацию и запустите ее.

mutation send {
     sendMessage(name: “User”, content: “Hello”){
        id
        name
        content
     }
}

Затем вы можете увидеть ответ на мутацию, а также ответы на подписку внизу. Вы можете запустить мутацию пару раз и посмотреть, как работают подписки при срабатывании событий. Посмотреть репозиторий GitHub с полным кодом можно здесь. Свяжитесь со мной, если у вас возникли проблемы с настройкой сервера.