Хорошо, это 2k18, и все хоть раз слышали о GraphQL. Может быть, от ваших коллег или даже от каких-нибудь гопников из неблагополучных районов вашего города. Шумиха вокруг GraphQL повсюду.

Вкратце, GraphQL - это язык запросов для вашего API, созданный Facebook в 2012 году и публично выпущенный в 2015 году. Он описывает возможности и требования с помощью системы типов, которую вы определяете для своих данных. Прозрачный? Конечно, все эти запросы и мутации, даже ваш дедушка уже знает и использует их. Вот почему мы не будем вдаваться в подробности об основах. Вместо этого мы начнем с добавления подписок в существующее серверное приложение.

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

Если вас интересуют подробности, я рекомендую выступление Ури Гольдштейна с ReactiveConf Я присутствовал несколько месяцев назад в Братиславе, Словакия.

Нет больше теории, давайте сразу перейдем к практике и создадим простой и масштабируемый сервер подписок GraphQL, используя среду Node.js Express, MongoDB в качестве базы данных и Flow.js для проверки статического типа.

Начнем с клонирования репозитория git и установки необходимых зависимостей.

git clone https://github.com/yakovlevyuri/graphql-subscriptions-server.git
cd graphql-subscriptions-server
cd yarn install

Я предполагал, что у вас уже установлен и запущен MongoDB. По умолчанию он работает на порту27017, поэтому мы будем использовать тот же порт для нашего приложения.

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

Итак, приступим к нашему серверу.

yarn start

Когда вы перейдете в браузере на http: // localhost: 8080 / Playground, вы должны войти в Playground:

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

Добавьте поддержку подписок на наш сервер GraphQL через WebSockets

Поскольку мы не можем отправлять частые обновления с сервера на клиент через HTTP, нам нужен сервер WebSocket. Чтобы создать его, мы воспользуемся пакетом subscriptions-transport-ws, который упрощает задачу. Добавьте необходимые операторы импорта в src/index.js

import { createServer } from 'http';
import { execute, subscribe } from 'graphql';
import { SubscriptionServer } from 'subscriptions-transport-ws';

Чтобы открыть WebSocket на сервере GraphQL, мы должны заключить сервер Express в createServer.

const app = express();
const server = createServer(app);

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

server.listen(PORT, () => {
  new SubscriptionServer(
    {
      execute,
      subscribe,
      schema: Schema,
    },
    {
      server: server,
      path: '/subscriptions',
    },
  );
console.log(`GraphQL Server is now running on ${BASE_URI}`);
  console.log(`Subscriptions are running on ${WS_BASE_URI}/subscriptions`);
});

Последнее, что нужно сделать на этом шаге, - настроить GraphQL Playground для использования только что настроенных подписок WebSocket. Мы используем его как промежуточное ПО для сервера, и пакетgraphql-playground-middleware-express - это все, что нам нужно. Если вы используете другую серверную среду (например, Hapi или Koa), вы также найдете подходящие пакеты здесь.

Импортируйте желаемый пакет:

import expressPlayground from 'graphql-playground-middleware-express';

И настройте конечные точки:

app.get(
  '/playground',
  expressPlayground({
    endpoint: '/graphql',
    subscriptionsEndpoint: `${WS_BASE_URI}/subscriptions`,
  }),
);

Объявление подписок в схеме и добавление преобразователя подписок

Теперь давайте определим подписки в нашей схеме GraphQL.

// RootSubscriptions.js
import { GraphQLObjectType } from 'graphql';
import NewCat from './NewCat';
import RemovedCat from './RemovedCat';
export default new GraphQLObjectType({
  name: 'RootSubscription',
  description: 'Root Subscription',
  fields: {
    newCat: NewCat,
    removedCat: RemovedCat,
  },
});

Сразу после этого мы создадим экземпляр PubSub для обработки тем подписки для нашего приложения.

// serverConfig.js
import { PubSub } from 'graphql-subscriptions';
export const pubsub = new PubSub();

Создание преобразователя подписки очень похоже на запросы и изменения. Единственное отличие состоит в том, что вместо передачи только функции resolve мы также передаем object с методом subscribe, который должен возвращать функцию asyncIterator из PubSub.

// newCat.js
import { pubsub } from '../serverConfig';
import GraphQLCat from '../outputs/Cat';
import type { Cat } from '../types/Cat';
export default {
  type: GraphQLCat,
  subscribe: () => pubsub.asyncIterator('newCat'),
  resolve: (payload: Cat) => payload,
};

Как только наш преобразователь подписки будет готов, мы можем начать публиковать в нем newCat. Для этого мы просто вызываем метод PubSub pubsub.publish('newCat', cat) внутри мутацииaddCat.

// addCat.js
export default {
  ...
  resolve: async (
    _: mixed,
    args: argsType,
    { Cat }: GraphqlContextType,
  ): Promise<CatType> => {
    const payload = {
      ...args,
      createdAt: new Date().toISOString(),
    };
    const cat = await new Cat(payload).save();
    
    pubsub.publish('newCat', cat);
    return cat;
  },
};

Вернемся к браузеру и, наконец, попробуем его, выполнив следующий запрос:

subscription newCat {
  newCat {
    id
    name
    nickName
    description
    createdAt
    avatarUrl
    age
  }
}

Игровая площадка теперь ожидает создания нового Cat, и как только сработает мутацияaddCat, вы должны увидеть ее в своем окне Игровая площадка!

mutation addCat {
  addCat(name: "Pussie Cat", nickName: "pussie", description: "Ooops, I killed a mouse", avatarUrl: "http://tachyons.io/img/cat-720.jpg", age: 5) {
    id
    name
    nickName
    description
    createdAt
    avatarUrl
    age
  }
}

Демонстрационное время

Поздравляю! Вы реализовали свои первые серверные подписки GraphQL через WebSockets! Вы можете найти полный исходный код этого примера на моем GitHub.

Кошки счастливы.

Понравился пост и хотите начать использовать GraphQL ежедневно? В настоящий момент мы ищем опытных фанатов JavaScript, которые присоединятся к нам на Kiwi.com в одном из самых красивых городов Европы - Праге. Для получения дополнительной информации свяжитесь со мной @ [email protected]