Skip to content

Latest commit

 

History

History
278 lines (216 loc) · 14.1 KB

GraphQL.md

File metadata and controls

278 lines (216 loc) · 14.1 KB

GraphQL

Что это?

Разработан в Facebook, 2012 г, выпущена в 2015.
С 2018 - некоммерческая организация
Создатель Ли Байрон.
Название отсылает к «графовым базам данных» (тип БД) и «языку запросов»(query language). Часть "Graph" отражает идею получения контента, проходя сквозь граф API, используя поля и подполя.

Определения

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

Позволяет получать данные из API и передавать от сервера клиенту.
Альтернатива REST API.

Некоторые преимущества

  • Вы получаете информацию именно в том объёме, в котором запрашиваете. Позволяет клиенту точно указать, какие данные ему нужны.
  • Вам будет необходима всего одна конечная точка.
  • Облегчает агрегацию данных из нескольких источников. Можно получить несколько ресурсов сервера одним вызовом, а не выполнять множество вызовов REST API.
  • GraphQL — сильно типизированный язык, что позволяет предварительно оценить корректность запроса в рамках системы типов синтаксиса.
  • GraphQL поддерживает веб-сокеты прямо из коробки в виде абстракций, называемых подписками.
    Веб-сокеты — прямое соединения между сервером и клиентом, чтобы сервер мог сообщать когда он обновляется.
    Вместо обращений клиента с запросом на обновление (используя Redux), сервер сообщает клиенту, что данные должны быть обновлены.

Зачем

Представьте, что вам нужно отобразить список записей (posts), и под каждым опубликовать список лайков (likes), включая имена пользователей и аватары. Это не сложно — вы просто измените API posts так, чтобы оно содержало массив likes, в котором будут объекты-пользователи.
Затем, при разработке мобильного приложения, оказалось что из-за загрузки дополнительных данных приложение работает слишком медленно. Теперь нужно два endpoint, один возвращающий записи с лайками, а другой без них.
Добавим ещё один фактор: оказывается, записи хранятся в базе данных MySQL, а лайки в Redis...
Facebook придумал концептуально простое решение: вместо того, чтобы иметь множество "глупых" endpoint, лучше иметь один "умный" endpoint, который будет способен работать со сложными запросами и придавать данным такую форму, какую запрашивает клиент.
Слой GraphQL находится между клиентом и одним или несколькими источниками данных; он принимает запросы клиентов и возвращает необходимые данные в соответствии с переданными инструкциями

Это инструмент, который объединяет возможности SQL и REST на стороне клиента. Берёт идеи, разработанные для манипуляции данными в БД, и использует их в вебе. Поэтому с помощью одного запроса GraphQL можно получить сразу все необходимые данные.


Отличия от GrpahQL

REST API — точка входа /users возвращает пользователей с заранее оговоренным набором полей.

GraphQL — на стороне клиента определяется какие данные нужны. Запрос идёт на единую точку, получает в ответе только нужные данные.


Основы

GraphQL API построен на трёх основных строительных блоках:

  • запросы (queries), мутации (mutation), подписки (subscription)
  • распознавателях (resolvers)
  • схема (schema)

Запросы (queries)
Структура вложенных друг в друга полей и массивов.
Поддерживаются аргументы (id: "123foo") и переменные (id: $id).

    query getMyPost($id: String) {
      post(id: $id){
        title
        body
        author{
          name
          avatarUrl
          profileUrl
        }
      }
    }

С помощью запросов GraphQL получает необходимые данные с сервера. Тип запроса Query в GraphQL — аналог GET в REST. Запросы — строки, которые отправляются в теле HTTP POST-запроса.
Все типы запросов в GraphQL отправляются через POST.

В ответ на этот запрос сервер присылает данные в формате JSON. Структура ответа соответствует структуре запроса.

Мутации (mutation)
С помощью этого типа операций можно добавлять данные в БД.
Аналог POST и PUT в REST.

mutation createUser{
  addUser(fname: "Richie", age: 22) {
    id
  }
}

Подписки (subscription)
С помощью этого типа операций клиент слушает изменения в БД в режиме реального времени.
Под капотом подписки используют websokets.

subscription listenLikes {
  listenLikes {
    fname
    likes
  }
}

Распознаватели (resolvers)
Сервер GraphQL не может знать что делать с входящим запросом, если ему не объяснить при помощи распознавателя (resolver) .
Используя распознаватель GraphQL понимает, как и где получить данные, соответствующие запрашиваемому полю.

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

С их помощью можно получать данные из REST API, базы данных или другого источника.

Пример распознавателя для 3 полей (post, author, commentsCount):

Query: {
  post(root, args) {
    return Posts.find({ id: args.id });
  }
},
Post: {
  author(post) {
    return Users.find({ id: post.authorId})
  },
  commentsCount(post) {
    return Comments.find({ postId: post.id}).count()
  }
}

Ключевое понятие здесь то, что схема запроса GraphQL и структура вашей базы данных никак не связаны. Другими словами, в базе данных может не существовать полей author или commentsCount, но мы можем "симулировать" их благодаря силе распознавателей.
Как было показано выше, вы можете писать любой код внутри распознавателя. Так что вы можете изменять содержимое базы данных; такие распознаватели называют изменяющими (mutation).

Схема (schema)
Все это становится возможным благодаря типизированной схеме данных GraphQL.
Схема состоит из двух взаимосвязанных объектов: TypeDefs и Resolvers.

У GraphQL есть лишь один endpoint /graphql.
Он может обладать несколькими endpoints, выполняющими различные действия. Они указаны в схеме.

Схема выполняет следующие действия:

  • Указывает различные endpoints
  • Определяет поля ввода и вывода для endpoint
  • Определяет действие, которое должно быть выполнено при достижении endpoint и так далее.


Как начать использовать? Экосистема

Необходимо всего два компонента чтобы начать:

  • Сервер GraphQL для обработки запросов к API
  • Клиент GraphQL, который будет подключаться к endpoint.

Сервера

  • GraphQL-JS (Node)
  • GraphQL-Server (Node)
  • Apollo

Клиенты
Конечно вы можете работать с API GraphQL напрямую, но специальная клиентская библиотека определённо может сделать вашу жизнь проще.

  • Relay - собственный инструментарий Facebook
  • Apollo - из 2 частей:
    • Apollo-client, позволяет выполнять запросы GraphQL в браузере (также есть расширение для DevTools)
    • коннектор для frontend-фреймворка (React-Apollo, Angular-Apollo и другие)
    • По умолчанию Apollo-client сохраняет данных используя Redux, который сам является достаточно авторитетной библиотекой управления состоянием с богатой экосистемой.

Прочее

  • VulcanJS - React/GraphQL
  • Gatsby - генератор статических сайтов для React, использует GraphQL
  • GraphiQL - браузерная IDE для создания и выполнения запросов к endpoint-ам GraphQL.


Ссылки



Legmo, 2019-2023

const mergeIntervals = (arr) => {
  if(arr.length > 0) {
    const arrCopy = arr.map( item => [...item]);
    let result = [];
    let isResultChanged = false;
    let isAIncludesB = ([a,b], [c,d]) => {return (a < c) && (b > d)};
    let isABIntersect = ([a,b], [c,d]) => {return ((b >= c) && (b <= d)) || ((a >= c) && (a <= d))};

    arrCopy.forEach(intervalAB => {
      if( result.length > 0) {
          
        for(let index = 0; index < result.length; index++) {
          let [a,b] = intervalAB;
          let [c,d] = result[index];

          console.log(
            `(${a}-${b}), ${c}-${d} —`,
            isAIncludesB([a,b], [c,d]),
            isAIncludesB([c,d], [a,b]),
            isABIntersect([a,b], [c,d]),
            '—',  (isAIncludesB([a,b], [c,d]) || isAIncludesB([c,d], [a,b]) || isABIntersect([a,b], [c,d]))
          )

          if (isAIncludesB([a,b], [c,d]) || isAIncludesB([c,d], [a,b]) || isABIntersect([a,b], [c,d])) {
            let x =  Math.min(a, c);
            let y =  Math.max(b, d);
            result[index]=([x,y]);
            isResultChanged = true;
            break
          }
          else {
            console.log('Else')
            result.push(intervalAB)
          }
        }
      }
      else {
        result.push(intervalAB)
      }
    });


    if((result.length > 1) && (isResultChanged)) {
      mergeIntervals(result)
    } else {
      console.log('result', result);
      return result;
    }
  }
};

mergeIntervals([[4, 8], [3, 5], [7, 12], [1, 2]]); // => [[3, 12], [1, 2]]
mergeIntervals([[3, 4], [1, 2], [4, 5], [2, 3]]); // => [[1, 5]]