Coder Social home page Coder Social logo

graphql's Introduction

GraphQl tutorail

GraphQL - це мова запитів, використовуваний клієнтськими додатками для роботи з даними. C GraphQL пов'язане таке поняття, як «схема» - це те, що дозволяє організовувати створення, читання, оновлення та видалення даних в вашому додатку (тобто - перед нами чотири базові функції, які використовуються при роботі зі сховищами даних, які зазвичай позначають акронімом CRUD - create, read, update, delete). GraphQL - це система, незалежна від джерел даних, тобто, для організації її роботи неважливо - де саме зберігаються дані.

Гнучкість - це те, що відрізняє технологію GraphQL від широко відомої технології REST. При використанні REST, якщо все зроблено правильно, кінцеві точки зазвичай створюють з урахуванням особливостей якогось ресурсу або типу даних програми.

Проблеми REST API:

Багато кінцевих точок (URLs) Кожен ресурс в REST представлений кінцевою точкою тобто URL. У реальних додатках у нас як правило з'являється багато кінцевих точок для великої кількості ресурсів. Якщо ви хочете зробити запит GET, вам знадобиться кінцева точка, специфічна для цього запиту, з конкретними параметрами. Якщо ви хочете зробити запит POST, вам знадобиться інша кінцева точка тільки для цього запиту.

GET     /posts
GET     /comments
POST    /post
POST    /user

В результаті у нас буде багато кінцевих точок (urls), що означає, що на розробку і підтримку всіх цих API буде витрачено багато часу розробників.

Надмірний витяг або неповне вилучення інформації Реальна проблема, яка дратує багатьох розробників, - це надмірна вибірка або навпаки неповна вибірка інформації через REST API. Це тому, що REST API завжди повертають заздалегідь створену фіксовану структуру. Ми не можемо отримати саме ті дані, які нам потрібні в поточний момент, якщо ми не створимо для цього конкретну кінцеву точку тобто url.

Таким чином, якщо нам потрібен тільки маленький шматочок даних, ми повинні працювати з усім об'єктом. Наприклад, якщо нам потрібно тільки отримати firstName, lastName і age користувача в REST API, ми не зможемо отримати саме ці дані без вилучення всього об'єкта.

Також існує проблема з неповним завантаженням інформації. Якщо ми хочемо отримати дані з двох різних ресурсів, нам потрібно зробити різні виклики з двох різних кінцевих точок. У величезному додатку це не так добре масштабується, так як будуть випадки, коли нам потрібно отримати тільки певний фрагмент даних, а не весь об'єкт. Тепер уявіть, що ми створюємо додаток, яке буде мати 100 кінцевих точок. Уявіть обсяг роботи і код, який нам потрібно написати. Все це може стати складніше згодом.

Версіювання Ще одним з найболючіших моментів в REST є версіювання. З REST API дуже часто можна побачити безліч API з v1 або v2, але в GraphQL це не потрібно, оскільки ви можете розвивати свої API, додаючи нові типи або видаляючи старі.

У GraphQL все, що вам потрібно для розвитку вашого API - це написати новий код. Ви можете писати нові типи, запити та мутації без необхідності постачати іншу версію вашого API.

Таким чином, ви не побачите API-інтерфейси GraphQL з кінцевими точками, подібними наступним:

https://example.com/api/v1/users/
https://example.com/api/v2/users/

Чому за GraphQL майбутнє Ще в 2012 році Facebook зіткнувся з проблемою, коли розробляв свої мобільні додатки, що призвело до створення GraphQL. Ці проблеми дуже поширені, особливо коли ми говоримо про розробку RESTful API. Як вже говорилося, ці проблеми:

  • Низька продуктивність
  • Безліч кінцевих точок
  • Надмірне витяг або неповне вилучення даних
  • Створення іншої версії кожен раз, коли нам потрібно змінити або видалити щось
  • Складність підтримки API

Маючи на увазі безліч концепцій, розробники з Facebook розробили кращий спосіб розробки API, а потім назвали його GraphQL. По суті, це заміна REST, з великою кількістю поліпшень.

З GraphQL ми отримуємо багато нових функцій, які дають вам надздібності при створенні ваших API. Давайте розглянемо їх один за іншим:

З GraphQL ви отримуєте тільки ті дані, які вам потрібні Немає більше надлишку або нестачі даних. Ви отримуєте тільки ті дані, які вам потрібні. Пам'ятайте проблеми з низькою продуктивністю, які ми обговорювали спочатку? У нас цього більше немає, так як GraphQL підвищує продуктивність вашого API, особливо в разі повільного з'єднання з мережею.

Як працює GraphQL? GraphQL, для передачі даних клієнтові і отримання їх від нього, покладається на прості GET або POST-запити. Якщо докладніше розглянути цю думку, то виявляється, що в GraphQL є два види запитів. До першого виду належать запити на читання даних, які в термінології GraphQL називаються просто запитами (query) і відносяться до букви R (reading, читання) акронима CRUD. Запити другого виду - це запити на зміну даних, які в GraphQL називають мутаціями (mutation). Вони відносяться до буксує C, U і D акронима CRUD, тобто - з їх допомогою виконують операції створення (create), поновлення (update) і видалення (delete) записів.

Всі ці запити і мутації відправляють на URL GraphQL-сервера, який, наприклад, може виглядати як https://myapp.com/graphql, у вигляді GET або POST-запитів.

Запити GraphQL Запити GraphQL - це сутності, що представляють собою запит до сервера на отримання деяких даних. Наприклад, у нас є якийсь призначений для користувача інтерфейс, який ми хочемо заповнити даними. За цими даними ми і звертаємося до сервера, виконуючи запит. При використанні традиційних REST API наш запит приймає вид GET-запиту. При роботі з GraphQL використовується новий синтаксис побудови запитів:

{
  flavors {
    name
  }
}

Це що, JSON? Або JavaScript-об'єкт? Ні те, ні інше. Як ми вже говорили, в назві технології GraphQL дві останні букви, QL, означають «query language», тобто - мова запитів. Мова йде, в буквальному сенсі, про нову мову написання запитів на отримання даних. Звучить все це як опис чогось досить складного, але насправді нічого складного тут немає. Розберемо вищенаведений запит:

{
  // Сюда помещают описания полей, которые нужно получить.

}

Всі запити починаються з «кореневого запиту», а то, що потрібно отримати в ході виконання запиту, називається полем. Для того щоб позбавити себе від плутанини, найкраще називати ці сутності «полями запиту в схемі».

{
  flavors {
    // Вложенные поля, которые мы хотим получить для каждого значения flavor.

  }
}

Запитуючи якесь поле, ми, крім того, повинні вказати вкладені поля, які потрібно отримати для кожного об'єкта, який приходить у відповіді на запит (навіть якщо очікується, що у відповідь на запит прийде всього один об'єкт).

{
  flavors {
    name
  }
}

Що в підсумку вийде? Після того, як ми відправимо такий запит GraphQL-сервера, ми отримаємо добре оформлений акуратний відповідь щось схоже на це:

{
  "data": {
    "flavors": [
      { "name": "The Lazy Person's Movie Theater" },
      { "name": "What's Wrong With You Caramel" },
      { "name": "Gnarly Chili Lime" }
    ]
  }
}
{
  flavors {
    id
    name
    description
  }
}

У відповідь на цей запит ми отримаємо наступне:

{
  "data": {
    "flavors": [
      { "id": 1, "name": "The Lazy Person's Movie Theater", description: "That elusive flavor that you begrudgingly carted yourself to the theater for, now in the comfort of your own home, you slob!" },
      { "id": 2, "name": "What's Wrong With You Caramel", description: "You're a crazy person that likes sweet popcorn. Congratulations." },
      { "id": 3, "name": "Gnarly Chili Lime", description: "A friend told me this would taste good. It didn't. It burned my kernels. I haven't had the heart to tell him." }
    ]
  }
}

GraphQL - дуже потужна технологія. Звертаємося ми до однієї і тієї ж кінцевої точки, а відповіді на запити в точності відповідають тому, що потрібно для наповнення тієї сторінки, з якої виконуються ці запити.

Якщо нам потрібно отримати лише один об'єкт flavor, то ми можемо скористатися тим фактом, що GraphQL вміє працювати з аргументами:

{
  flavors(id: "1") {
    id
    name
    description
  }
}

Тут ми жорстко поставили в коді конкретний ідентифікатор (id) об'єкта, відомості про який нам потрібні, але в подібних випадках можна використовувати і динамічні ідентифікатори:

query getFlavor($id: ID) {
  flavors(id: $id) {
    id
    name
    description
  }
}

Тут, в першому рядку, ми даємо запитом ім'я (ім'я вибирається довільним чином, getFlavor можна замінити на щось на кшталт pizza, і запит залишиться працездатним) і оголошуємо змінні, які очікує запит. В даному випадку мається на увазі, що запит буде переданий ідентифікатор (id) скалярного типу ID (про типах ми поговоримо нижче).

Незалежно від того, статичний або динамічний id використовується при виконанні запиту, ось як буде виглядати відповідь на подібний запит:

{
  "data": {
    "flavors": [
      { "id": 1, "name": "The Lazy Person's Movie Theater", description: "That elusive flavor that you begrudgingly carted yourself to the theater for, now in the comfort of your own home, you slob!" }
    ]
  }
}

Як бачите, все влаштовано дуже зручно. Ймовірно, ви вже починаєте міркувати про застосування GraphQL у власному проекті. І, хоча те, про що ми вже говорили, виглядає чудово, краса GraphQL по-справжньому проявляється там, де працюють з вкладеними полями. Припустимо, що в нашій схемі є ще одне поле, яке називається nutrition і містить відомості про харчову цінність різних видів попкорну:

{
  flavors {
    id
    name
    nutrition {
      calories
      fat
      sodium
    }
  }
}

Може здатися, що в нашому сховищі даних, в кожному об'єкті flavor, міститиметься вкладений об'єкт nutrition. Але це не зовсім так. Використовуючи GraphQL можна комбінувати звернення до самостійних, але пов'язаних джерел даних в одному запиті, що дозволяє отримувати відповіді, що дають зручність роботи з вкладеними даними без необхідності денормалізації бази даних:

{
  "data": {
    "flavors": [
      {
        "id": 1,
        "name": "The Lazy Person's Movie Theater",
        "nutrition": {
          "calories": 500,
          "fat": 12,
          "sodium": 1000
        }
      },
      ...

    ]
  }
}

Мутації GraphQL У той час як запити GraphQL виконують завантаження даних, мутації відповідальні за внесення в дані змін. Мутації можуть бути використані у вигляді базового механізму RPC (Remote Procedure Call, виклик віддалених процедур) для вирішення різних завдань на зразок відправки даних користувача API, розроблений третьою.

При описі мутацій використовується синтаксис, що нагадує той, який ми застосовували при формуванні запитів:

mutation updateFlavor($id: ID!, $name: String, $description: String) {
  updateFlavor(id: $id, name: $name, description: $description) {
    id
    name
    description
  }
}

Тут ми оголошуємо мутацію updateFlavor, вказуючи деякі змінні - id, name і description. Діючи за тією ж схемою, яка застосовується при описі запитів, ми «оформляємо» змінювані поля (кореневу мутацію) за допомогою ключового слова mutation, за яким слідує ім'я, яке описує мутацію, і набір змінних, які потрібні для формування відповідного запиту на зміну даних.

Ці змінні включають в себе те, що ми намагаємося змінити, або те, мутацію чого ми хочемо викликати. Зверніть увагу також і на те, що після виконання мутації ми можемо отримати відшкодування деяких полів.

В даному випадку нам потрібно отримати, після зміни запису, поля id, name і description. Це може стати в нагоді при розробці чогось на кшталт оптимістичних інтерфейсів, позбавляючи нас від необхідності виконувати запит на отримання змінених даних після їх зміни.

Розробка схеми і підключення її до GraphQL-серверу Для того, щоб виконати GraphQL-запит, потрібен GraphQL-сервер, з яким можна такий запит відправити. GraphQL-сервер являє собою звичайний HTTP-сервер (якщо ви пишете на JavaScript - то це може бути сервер, створений за допомогою Express або Hapi), до якого приєднана GraphQL-схема.

import express from 'express'
import graphqlHTTP from 'express-graphql'
import schema from './schema'

const app = express()

app.use('/graphql', graphqlHTTP({
  schema: schema,
  graphiql: true
}))

app.listen(4000)

Під «приєднанням» схеми ми розуміємо механізм, який пропускає через схему запити, отримані від клієнта, і повертає йому відповіді. Це схоже на повітряний фільтр, через який повітря надходить в приміщення.

Процес «фільтрації» пов'язаний із запитами або мутаціями, що відправляються клієнтом на сервер. І запити і мутації вирішуються з використанням функцій, пов'язаних з полями, певними в кореневому запиті або в кореневій мутації схеми.

Вище наведено приклад каркаса HTTP-сервера, створеного за допомогою JavaScript-бібліотеки Express. Використовуючи функцію graphqlHTTP з пакета express-graphql від Facebook, ми «прикріплюємо» схему (передбачається, що вона описана в окремому файлі) і запускаємо сервер на порту 4000. Тобто, клієнти, якщо говорити про локальний використанні цього сервера, зможуть відправляти запити по адресою http://localhost:4000/graphql.

Типи даних і розпізнавачі Для того щоб забезпечити роботу GraphQL-сервера, потрібно підготувати схему і приєднати її до нього.

import gql from 'graphql-tag'
import mongodb from '/path/to/mongodb’ // Это - лишь пример. Предполагается, что `mongodb` даёт нам подключение к MongoDB.


const schema = {
  typeDefs: gql`
    type Nutrition {
      flavorId: ID
      calories: Int
      fat: Int
      sodium: Int
    }

    type Flavor {
      id: ID
      name: String
      description: String
      nutrition: Nutrition
    }

    type Query {
      flavors(id: ID): [Flavor]
    }

    type Mutation {
      updateFlavor(id: ID!, name: String, description: String): Flavor
    }
  `,
  resolvers: {
    Query: {
      flavors: (parent, args) => {
        // Предполагается, что args равно объекту, наподобие { id: '1' }
        return mongodb.collection('flavors').find(args).toArray()
      },
    },
    Mutation: {
      updateFlavor: (parent, args) => {
        // Предполагается, что args равно объекту наподобие { id: '1', name: 'Movie Theater Clone', description: 'Bring the movie theater taste home!' }

        // Выполняем обновление.

        mongodb.collection('flavors').update(args)

        // Возвращаем flavor после обновления.

        return mongodb.collection('flavors').findOne(args.id)
      },
    },
    Flavor: {
      nutrition: (parent) => {
        return mongodb.collection('nutrition').findOne({
          flavorId: parent.id,
        })
      }
    },
  },
}

export default schema

Визначення полів в схемі GraphQL складається з двох частин - з оголошень типів (typeDefs) і распознавателей (resolver). Сутність typeDefs містить оголошення типів для даних, які використовуються в додатку. Наприклад, раніше ми говорили про запит на отримання з сервера списку об'єктів flavor. Для того щоб до нашого сервера можна було б виконати подібний запит, потрібно зробити наступні три кроки:

  • Повідомити схемою про те, як виглядають дані об'єктів flavor (в прикладі, наведеному вище, це виглядає як оголошення типу type Flavor).
  • Оголосити поле в кореневому поле type Query (це властивість flavors значення type Query).
  • Оголосити функцію-распознаватель об'єкта resolvers.Query, написану відповідно до полями, оголошеними в кореневому поле type Query.

Звернемо тепер увагу на typeDefs. Тут ми повідомляємо схемою відомості про форму (shape) наших даних. Іншими словами, ми повідомляємо GraphQL про різні властивості, які можуть міститися в сутності відповідного типу.

type Flavor {
  id: ID
  name: String
  description: String
  nutrition: Nutrition
}

Оголошення type Flavor вказує на те, що об'єкт flavor може містити поле id типу ID, поле name типу String, поле description типу String і поле nutrition типу Nutrition.

У випадку з nutrition ми використовуємо тут ім'я іншого типу, оголошеного в typeDefs. Тут конструкція type Nutrition описує форму даних про харчову цінність попкорну.

Зверніть увагу на те, що ми тут, як і на самому початку цього матеріалу, говоримо про «додатку», а не про «базі даних». У вищенаведеному прикладі передбачається, що у нас є база даних, але дані в додаток можуть надходити з будь-якого джерела. Це може бути навіть API, розроблений третьою або статичний файл.

Так само, як ми вступали до оголошенні type Flavor, тут ми вказуємо імена полів, які будуть міститися в об'єктах nutrition, використовуючи, як типів даних цих полів (властивостей) то, що в GraphQL називається скалярними типами даних. На момент написання цього матеріалу в GraphQL підтримувалося 5 вбудованих скалярних типів даних:

  • Int: ціле 32-бітове число зі знаком.
  • Float: число подвійної точності з плаваючою крапкою із знаком.
  • String: послідовність символів в кодуванні UTF-8.
  • Boolean: логічне значення true або false.
  • ID: унікальний ідентифікатор, часто використовуваний для багатократного завантаження об'єктів або в якості ключа в кеші. Значення типу ID серіалізуются так само, як рядки, однак вказівка на те, що якесь значення має тип ID, підкреслює той факт, що це значення призначене не для показу його людям, а для використання в програмах.

На додаток до цих скалярним типам ми можемо призначати властивостям і типи, певні нами самостійно. Саме так ми вчинили, призначивши властивості nutrition, описаного в конструкції type Flavor, тип Nutrition.

type Query {
  flavors(id: ID): [Flavor]
}

У конструкції type Query, в якій описується кореневої тип Query (той «кореневої запит», про який ми говорили раніше), ми оголошуємо ім'я поля, яке може бути запитано. Оголошуючи про це поле, ми, крім того, разом з типом даних, який очікуємо повернути, вказуємо аргументи, які можуть надійти в запиті.

В даному прикладі ми очікуємо можливого надходження аргументу id скалярного типу ID. Як відповідь на такий запит очікується масив об'єктів, пристрій яких нагадує пристрій типу Flavor.

Підключення распознавателя запитів Тепер, коли в кореневому type Query є визначення поля field, нам потрібно описати те, що називається функцією-розпізнавачем.

Це - те місце, де GraphQL, більш-менш, «зупиняється». Якщо ми подивимося на об'єкт resolvers схеми, а потім на об'єкт Query, вкладений в нього, ми можемо побачити там властивість flavors, якій призначена функція. Ця функція і називається розпізнавачем для поля flavors, яке оголошено в кореневому type Query.

typeDefs: gql`…`,
resolvers: {
  Query: {
    flavors: (parent, args) => {
      // Предполагается, что args равно объекту наподобие { id: '1' }
      return mongodb.collection('flavors').find(args).toArray()
    },
  },},

Ця функція-распознаватель приймає кілька аргументів. Аргумент parent - це батьківський запит, якщо такий існує, аргумент args теж передається запитом в тому випадку, якщо він існує. Тут ще може використовуватися аргумент context, який в нашому випадку не представлений. Він дає можливість працювати з різними «контекстними» даними (наприклад - в якому показується користувача в тому випадку, якщо сервер підтримує систему облікових записів користувачів).

Усередині распознавателя ми робимо все, що потрібно для того, щоб виконати запит. Саме тут GraphQL «перестає турбуватися» про те, що відбувається і дозволяє нам виконувати завантаження і повернення даних. Тут, повторимося, можна працювати з будь-якими джерелами даних.

Хоча GraphQL і не цікавлять джерела надходження даних, цю систему надзвичайно сильно цікавить те, що саме ми повертаємо. Ми можемо повернути JSON-об'єкт, масив JSON-об'єктів, промис (його дозвіл GraphQL бере на себе).

Тут ми використовуємо мок-звернення до колекції flavors бази даних MongoDB, передаючи args (якщо відповідний аргумент переданий розпізнавачів) в виклик .find () і повертаючи то, що буде знайдено в результаті виконання цього виклику, у вигляді масиву.

Отримання даних для вкладених полів Вище ми вже розібрали дещо, що відноситься до GraphQL, але зараз, можливо, поки незрозуміло те, як бути з вкладеним полем nutrition. Пам'ятайте про те, що дані, представлені полем Nutrition, ми, насправді, не бережемо спільно з основними даними, що описують сутність flavor. Ми виходимо з припущення про те, що ці дані зберігаються в окремій колекції / таблиці бази даних.

Хоча ми повідомили GraphQL про те, що type Flavor може включати в себе дані nutrition в формі type Nutrition, ми не пояснили системі порядок отримання цих даних зі сховища. Ці дані, як уже було сказано, зберігаються окремо від даних сутностей flavor.

typeDefs: gql`
    type Nutrition {
      flavorId: ID
      calories: Int
      fat: Int
      sodium: Int
    }

    type Flavor {
      […]
      nutrition: Nutrition
    }

    type Query {…}

    type Mutation {…}
  `,
  resolvers: {
    Query: {
      flavors: (parent, args) => {},
    },
    Mutation: {},
    Flavor: {
      nutrition: (parent) => {
        return mongodb.collection('nutrition').findOne({
          flavorId: parent.id,
        })
      }
    },
  },

Якщо придивитися уважніше до об'єкта resolvers в схемі, то можна помітити, що тут містить додаткові об'єкти Query, Mutation і Flavor. Вони відповідають типам, які ми оголосили вище в typeDefs.

Якщо подивитися на об'єкт Flavors, то виявиться, що поле nutrition в ньому оголошено як функція-распознаватель. Помітною особливістю такого рішення є той факт, що ми оголошуємо функцію безпосередньо в типі Flavor. Іншими словами, ми говоримо системі: «Ми хочемо, щоб ти завантажила поле nutrition саме так для будь-якого запиту, що використовує type Flavor».

У цій функції ми виконуємо звичайний запит до MongoDB, але тут зверніть увагу на те, що ми використовуємо аргумент parent, переданий функції-розпізнавача. Те, що представлено тут аргументом parent, являє собою те, що міститься в полях, наявних у flavors. Наприклад, якщо нам потрібні всі сутності flavor, ми виконаємо такий запит:

{
  flavors {
    id
    name
    nutrition {
      calories
    }
  }
}

Кожне поле flavor, повернутий з flavors, ми пропустимо через распознаватель nutrition, при цьому дане значення буде представлено аргументом parent. Якщо придивитися до цієї конструкції, то виявиться, що ми, в запиті до MongoDB, використовуємо поле parent.id, яке представляє собою id суті flavor, обробкою якої ми займаємося в даний момент.

Ідентифікатор parent.id передається в запиті до бази даних, де проводиться пошук запису nutrition з ідентифікатором flavorId, яка відповідає оброблюваної суті flavor.

Підключення мутацій Те, що ми вже знаємо про запити, добре переноситься і на мутації. Насправді, процес підготовки мутацій практично повністю збігається з процесом підготовки запитів. Якщо поглянути на кореневу сутність type Mutation, то можна побачити, що ми оголосили в ній поле updateFlavor, що приймає аргументи, що задаються на клієнті

type Mutation {
  updateFlavor(id: ID!, name: String, description: String): Flavor
}

Цей код можна розшифрувати так: «Ми очікуємо, що мутація updateFlavor приймає id типу ID (знак оклику,!, Повідомляє GraphQL про те, що це поле необхідно), name типу String і description типу String». Крім того, після завершення виконання мутації ми очікуємо повернення деяких даних, структура яких нагадує тип Flavor (тобто - об'єкт, який містить властивості id, name, description, і, можливо, nutrition).

{
  typeDefs: gql`…`,
  resolvers: {
    Mutation: {
      updateFlavor: (parent, args) => {
        // Предполагается, что args равно объекту наподобие { id: '1', name: 'Movie Theater Clone', description: 'Bring the movie theater taste home!' }

        // Выполняем обновление.

        mongodb.collection('flavors').update(
          { id: args.id },
          {
            $set: {
              ...args,
            },
          },
        )

        // Возвращаем flavor после обновления.

        return mongodb.collection('flavors').findOne(args.id)
      },
    },
  },
}

Усередині функції-розпізнавача для мутації updateFlavor ми робимо саме те, чого від подібної функції можна очікувати: організуємо взаємодію з базою даних для того, щоб змінити в ній, тобто - оновити, відомості про що цікавить нас сутності flavor.

Зверніть увагу на те, що відразу ж після завершення оновлення оновлення ми виконуємо звернення до бази даних для того, щоб знову знайти ту ж сутність flavor і повернути її з распознавателя. Чому це так?

Згадайте про те, що на клієнті ми очікуємо отримати об'єкт в тому стані, в яке він був приведений після завершення мутації. В даному прикладі ми очікуємо, що буде повернута сутність flavor, яку ми тільки що оновили.

Чи можна просто повернути об'єкт args? Так можна. Причина, по якій ми вирішили в даному випадку цього не робити, полягає в тому, що ми хочемо бути на 100% впевненими в тому, що операція оновлення інформації в базі даних пройшла успішно. Якщо ми прочитаємо з бази даних інформацію, яка повинна бути зміненою, і виявиться, що вона і справді змінена, тоді можна зробити висновок про те, що операція пройшла успішно.

Навіщо може знадобитися використовувати GraphQL? Хоча те, створенням чого ми тільки що займалися, виглядає не дуже масштабно, зараз у нас є функціонуюче, хоча і просте, GraphQL-API.

Як і у випадку з будь-якої нової технологією, після першого знайомства з GraphQL ви можете задатися питанням про те, навіщо вам може стати в нагоді щось подібне. Чесно кажучи, на це питання не можна дати однозначної і простої відповіді. Дуже вже багато всього потрібно врахувати для того, щоб така відповідь знайти. І можна, до речі, подумати про те, щоб замість GraphQL просто вибрати перевірену часом технологію REST або безпосередньо звертатися до бази даних. Власне кажучи, ось кілька ідей, над якими варто подумати в пошуках відповіді на питання про те, чи потрібна вам технологія GraphQL.

Ви прагнете зменшити кількість запитів, які виконуються з клієнта Многие приложения страдают от того, что им приходится выполнять слишком много HTTP-запросов, от того, что делать это приходится слишком часто, и от того, что это — сложные запросы. В то время как использование технологии GraphQL не позволяет полностью отказаться от выполнения запросов, эта технология, если ей правильно пользоваться, способна значительно уменьшить количество запросов, выполняемых со стороны клиента (во многих случаях для получения некоего набора связанных данных достаточно лишь одного запроса).

Является ли ваш проект приложением с множеством пользователей, или приложением, обрабатывающим огромные объёмы данных (например — это нечто вроде системы для работы с медицинскими данными), использование GraphQL определённо улучшит производительность его клиентской части.

Ви хочете уникнути денормалізації даних, що проводиться лише заради того, щоб оптимізувати роботу механізмів побудови призначеного для користувача інтерфейсу У додатках, в яких використовуються великі обсяги реляційних даних, часто може виникати «пастка денормалізації». Хоча такий підхід і виявляється робочим, він, поза всяким сумнівом, далекий від ідеалу. Його застосування може погано впливати на продуктивність систем. Завдяки використанню GraphQL і вкладених запитів необхідність в денормалізації даних значно зменшується.

У вас есть множество источников информации, к которым вы обращаетесь из разных приложений Ця проблема може бути частково вирішена за допомогою традиційних REST API, але навіть при такому підході одна проблема все ще залишається: однаковість запитів, які виконуються з клієнтської сторони. Припустимо, що в ваш проект входять веб-додаток, додатки для iOS і Android, а також API для розробників. У подібних умовах вам, найімовірніше, доведеться, на кожній платформі, «майструвати з підручних матеріалів» кошти для виконання запитів.

Це веде до того, що доводиться підтримувати, на різних платформах, кілька реалізацій HTTP, це означає відсутність однаковості в засобах виконання запитів і наявність заплутаних кінцевих точок API (ви, напевно, таке вже бачили).

Може бути технологія GraphQL - це верх досконалості? Чи варто мені прямо зараз викинути мій REST API і перейти на GraphQL? Ні звичайно. Ніщо не досконале. І, треба зазначити, працювати з GraphQL не так вже й просто. Для того щоб створити працюючу схему GraphQL, потрібно виконати безліч обов'язкових кроків. Так як ви тільки вивчаєте цю технологію, це може вивести вас з рівноваги, так як нелегко буває зрозуміти те, чого саме не вистачає у вашій схемі для правильної роботи системи. При цьому повідомлення про помилки, що виникають на клієнті і на сервері, можуть виявитися не особливо корисними.

Далі, використання GraphQL на клієнті, в тому, що виходить за рамки мови запитів, які не стандартизовано. Хоча роботу з GraphQL можуть полегшити різні бібліотеки, найпопулярнішими з яких є Apollo і Relay, кожна з них відрізняється власними специфічними особливостями.

GraphQL - це, крім того, лише специфікація. Пакети на кшталт graphql (цей пакет використовується всередині пакету express-graphql, застосованого в нашому прикладі) - це всього лише реалізації даної специфікації. Іншими словами, різні реалізації GraphQL для різних мов програмування можуть по-різному інтерпретувати специфікацію. Це може привести до виникнення проблем, чи йде мова про розробника-одиночці, або про команду, в якій, при роботі над різними проектами, використовуються різні мови програмування.


Незважаючи на те, що впровадження GraphQL може виявитися непростим завданням, ця технологія являє собою вражаючий крок вперед у сфері обробки даних. GraphQL не можна назвати ліками від усіх хвороб, але з цією технологією, безумовно, варто поекспериментувати. Почати можна, наприклад, поміркувавши про найзаплутанішій і неохайною підсистемі, використовуваної в вашому проекті при роботі з даними, і спробувавши реалізувати цю підсистему засобами GraphQL.

До речі, тут у мене для вас приємна новина: GraphQL можна реалізовувати інкрементного. Для того щоб отримати вигоди з застосування цієї технології немає потреби переводити на GraphQL абсолютно все. Так, поступово вводячи в проект GraphQL, можна розібратися з цією технологією самому, зацікавити команду, і, якщо те, що вийде, всіх влаштує, рухатися далі.

Головне - пам'ятайте про те, що GraphQL - це, в кінцевому рахунку, всього лише інструмент. Застосування GraphQL не означає необхідності в повній переробці всього, що було раніше. При цьому треба зазначити, що GraphQL - це технологія, з якою, безумовно, варто познайомитися. Багатьом варто подумати і про застосування цієї технології в своїх проектах. Зокрема, якщо ваші проекти здаються не дуже продуктивними, якщо ви займаєтеся розробкою складних інтерфейсів, на зразок панелей управління, стрічок новин або профілів користувачів, то ви вже знаєте про те, де саме ви можете випробувати GraphQL

graphql's People

Contributors

olegkozlovskui avatar

Watchers

James Cloos avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.