15%

Збережіть 15% на всі хостинг-послуги

Перевірте свої навички і отримайте Знижку на будь-який план хостингу

Використовуй код:

Skills
Почати
01.11.2024
1 +1

Повний посібник з GraphQL: Створюйте швидші, розумніші API на високопродуктивному хостингу

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

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

Зміст

  1. Що таке GraphQL?
  2. GraphQL проти REST: чому це важливо
  3. Ключові особливості GraphQL
  4. Налаштування GraphQL сервера
  5. Визначення вашої схеми
  6. Реалізація Resolvers
  7. Запити та мутація даних
  8. Оновлення в реальному часі за допомогою підписок
  9. GraphQL Introspection та інструменти розробника
  10. Розгортання GraphQL у виробництві
  11. Найкращі практики безпеки
  12. Висновок

1. Що таке GraphQL? {#what-is-graphql}

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

Цей підхід усуває дві найпостійніші проблеми у проектуванні REST API:

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

За допомогою GraphQL клієнт визначає форму відповіді. Сервер виконує контракт, визначений схемою, а клієнт запитує лише те, що він має намір використовувати.

2. GraphQL проти REST: чому це важливо {#graphql-vs-rest}

Розуміння того, коли вибрати GraphQL замість REST — і навпаки — критично важливо для прийняття обґрунтованих архітектурних рішень.

ВимірRESTGraphQL
Кінцеві точкиКілька (одна на ресурс)Одна уніфікована кінцева точка
Отримання данихФіксована структура відповідіПоля, вказані клієнтом
Надмірне отримання данихПоширенеУсунено за дизайном
Недостатнє отримання данихПоширене (проблема N+1)Вирішено вкладеними запитами
ВерсіонуванняПотрібне версіонування URL або заголовкаЕволюція схеми без версіонування
Підтримка реального часуПотребує WebSockets або pollingВбудовані підписки
Система типівНеобов’язкова (OpenAPI/Swagger)Вбудована, обов’язкова схема
КешуванняКешування на рівні HTTP простеПотребує кешування, що враховує запити

GraphQL особливо потужний для:

  • Складних, взаємопов’язаних моделей даних, де один вид потребує даних з кількох ресурсів.
  • Мобільних додатків, де мінімізація розміру корисного навантаження безпосередньо покращує користувацький досвід та зменшує витрати на дані.
  • Швидкої ітерації продукту, де команди фронтенду повинні розвивати свої вимоги до даних без очікування змін API бекенду.
  • Агрегації мікросервісів, де шлюз GraphQL поєднує кілька нижчестоящих сервісів в єдину поверхню API.

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

3. Ключові особливості GraphQL {#key-features}

3.1 Точне отримання даних

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

Це трансформаційно для команд, які будують продукти на кількох платформах — веб, iOS, Android, смарт-ТВ — де кожна поверхня має різні потреби в даних. Замість того, щоб підтримувати окремі REST кінцеві точки або приймати надуті корисні навантаження, кожен клієнт надсилає адаптований запит і отримує адаптовану відповідь.

3.2 Строго типізована схема

Кожен GraphQL API підтримується схемою, написаною на мові визначення схеми GraphQL (SDL). Схема є авторитетним контрактом між сервером та кожним клієнтом, який його використовує. Вона визначає:

  • Типи — форму кожного об’єкта у вашій моделі даних.
  • Запити — операції читання, які можуть виконувати клієнти.
  • Мутації — операції запису (створення, оновлення, видалення).
  • Підписки — потоки подій у реальному часі.
  • Зв’язки — як типи посилаються один на одного.

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

3.3 Оновлення в реальному часі за допомогою підписок

Механізм підписки GraphQL дозволяє постійні, керовані подіями з’єднання між клієнтом та сервером — зазвичай реалізовані через WebSockets. Коли на сервері відбувається подія, на яку підписано (нове повідомлення опубліковано, ціна акції змінюється, статус замовлення оновлюється), сервер негайно надсилає відповідні дані всім підписаним клієнтам.

Це робить GraphQL природним вибором для:

  • Додатків для живого чату та обміну повідомленнями
  • Інструментів спільного редагування
  • Фінансових інформаційних панелей з даними живого ринку
  • Сповіщень у реальному часі та стрічок активності
  • Синхронізації стану багатокористувацької гри

3.4 Introspection

GraphQL API є самодокументуючимися за дизайном. Система introspection дозволяє клієнтам запитувати саму схему — виявляючи доступні типи, поля, запити, мутації та їх описи під час виконання. Ця можливість живить інструменти розробника, такі як GraphiQL та Apollo Studio, які надають інтерактивні дослідники API, конструктори запитів та автоматичне генерування документації без будь-яких додаткових зусиль від автора API.

3.5 Еволюція схеми без версіонування

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

4. Налаштування GraphQL сервера {#setting-up}

GraphQL не залежить від мови. Зрілі бібліотеки серверів існують у всьому стеку технологій. Ось найширше використовувані варіанти:

Мова / RuntimeБібліотека / Фреймворк
Node.jsApollo Server, GraphQL Yoga, Express-GraphQL
PythonStrawberry, Graphene
JavaSpring for GraphQL, graphql-java
Gogqlgen, graphql-go
Rubygraphql-ruby
PHPLighthouse (Laravel), webonyx/graphql-php
Rustasync-graphql
.NET / C#Hot Chocolate, GraphQL.NET

Крок за кроком: Node.js з Apollo Server

Apollo Server — це найширше розгорнутий GraphQL сервер в екосистемі Node.js. Наступний посібник переводить вас від нуля до запущеного сервера.

Передумови:

  • Node.js 18 або пізніша версія встановлена
  • npm або yarn менеджер пакетів

Крок 1: Ініціалізуйте ваш проект

mkdir graphql-api && cd graphql-api
npm init -y
npm install @apollo/server graphql

Крок 2: Створіть файл вашого сервера

Створіть файл з назвою index.js (або index.mjs для ES модулів):

import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';

// Step 1: Define your type definitions (schema)
const typeDefs = `#graphql
  type Book {
    id: ID!
    title: String!
    author: String!
    publishedYear: Int
    genre: String
  }

  type Query {
    books: [Book!]!
    book(id: ID!): Book
  }
`;

// Step 2: Define your data source (in-memory for this example)
const books = [
  {
    id: '1',
    title: 'The Pragmatic Programmer',
    author: 'David Thomas & Andrew Hunt',
    publishedYear: 1999,
    genre: 'Technology',
  },
  {
    id: '2',
    title: 'Clean Code',
    author: 'Robert C. Martin',
    publishedYear: 2008,
    genre: 'Technology',
  },
];

// Step 3: Define your resolvers
const resolvers = {
  Query: {
    books: () => books,
    book: (_, { id }) => books.find((b) => b.id === id),
  },
};

// Step 4: Create and start the server
const server = new ApolloServer({ typeDefs, resolvers });

const { url } = await startStandaloneServer(server, {
  listen: { port: 4000 },
});

console.log(`🚀 GraphQL server ready at: ${url}`);

Крок 3: Запустіть сервер

node index.js
# Output: 🚀 GraphQL server ready at: http://localhost:4000/

Перейдіть до http://localhost:4000/ у вашому браузері, щоб відкрити Apollo Sandbox — інтерактивний дослідник запитів, де ви можете негайно протестувати ваш API.

5. Визначення вашої схеми {#defining-schema}

Схема — це основа кожного GraphQL API. Інвестування часу в добре розроблену схему окупається протягом усього життя вашого додатку.

Скалярні типи

GraphQL включає п’ять вбудованих скалярних типів:

    Int — 32-бітне ціле число зі знаком
    Float — число з плаваючою комою подвійної точності
    String — послідовність символів UTF-8
    Boolean — true або false
  • ID — унікальний ідентифікатор, серіалізований як рядок
  • Ви також можете визначити користувацькі скаляри для типів, таких як Date, DateTime, Email, URL або JSON.

    Типи об’єктів

    type Author {
      id: ID!
      name: String!
      biography: String
      books: [Book!]!
    }
    
    type Book {
      id: ID!
      title: String!
      author: Author!
      publishedYear: Int
      genre: String
      tags: [String!]
    }

    Модифікатор ! позначає поле, яке не може бути нульовим. [Book!]! означає список, який не може бути нульовим, об’єктів Book, які не можуть бути нульовими.

    Запити

    type Query {
      books: [Book!]!
      book(id: ID!): Book
      authors: [Author!]!
      author(id: ID!): Author
      booksByGenre(genre: String!): [Book!]!
    }

    Мутації

    type Mutation {
      createBook(title: String!, authorId: ID!, genre: String): Book!
      updateBook(id: ID!, title: String, genre: String): Book
      deleteBook(id: ID!): Boolean!
    }

    Типи введення

    Для мутацій з кількома аргументами типи введення зберігають вашу схему чистою та повторно використовуваною:

    input CreateBookInput {
      title: String!
      authorId: ID!
      publishedYear: Int
      genre: String
      tags: [String!]
    }
    
    type Mutation {
      createBook(input: CreateBookInput!): Book!
    }

    6. Реалізація Resolvers {#implementing-resolvers}

    Resolvers — це функції, які виконують кожне поле у вашій схемі. Кожне поле у схемі GraphQL може мати resolver. Якщо для поля не визначено resolver, GraphQL повертається до resolver за замовчуванням, який просто повертає властивість з тією ж назвою з батьківського об’єкта.

    Сигнатура Resolver

    fieldName: (parent, args, context, info) => value
    • parent — розв’язана значення батьківського типу (корисна для вкладених resolvers).
    • args — аргументи, передані до поля у запиті.
    • context — спільний об’єкт, переданий через весь ланцюг resolver, зазвичай містить аутентифікованого користувача, з’єднання з базою даних або завантажувачі даних.
    • info — метаінформація про виконання запиту, включаючи назву поля та схему.

    Приклад: Resolvers з базою даних

    const resolvers = {
      Query: {
        books: async (_, __, { db }) => {
          return db.collection('books').find().toArray();
        },
        book: async (_, { id }, { db }) => {
          return db.collection('books').findOne({ _id: id });
        },
      },
    
      Mutation: {
        createBook: async (_, { input }, { db, user }) => {
          if (!user) throw new Error('Authentication required');
          const result = await db.collection('books').insertOne(input);
          return { id: result.insertedId, ...input };
        },
      },
    
      Book: {
        // Nested resolver: fetch the author for each book
        author: async (book, _, { db }) => {
          return db.collection('authors').findOne({ _id: book.authorId });
        },
      },
    };

    Уникнення проблеми N+1 за допомогою DataLoader

    Вкладені resolvers можуть спровокувати проблему запиту N+1 — отримання списку 100 книг, а потім виконання 100 окремих викликів бази даних для розв’язання автора кожної книги. Рішення — DataLoader, утиліта для групування та кешування:

    import DataLoader from 'dataloader';
    
    // In your context factory:
    const authorLoader = new DataLoader(async (authorIds) => {
      const authors = await db.collection('authors')
        .find({ _id: { $in: authorIds } })
        .toArray();
      return authorIds.map((id) => authors.find((a) => a._id === id));
    });
    
    // In your resolver:
    Book: {
      author: (book, _, { authorLoader }) => authorLoader.load(book.authorId),
    }

    DataLoader групує всі author пошуки в одному тику циклу подій в один запит до бази даних, зменшуючи 100 запитів до 1.

    7. Запити та мутація даних {#querying-data}

    Базовий запит

    query GetAllBooks {
      books {
        id
        title
        author {
          name
        }
        genre
      }
    }

    Запит з аргументами

    query GetBook {
      book(id: "1") {
        title
        author {
          name
          biography
        }
        publishedYear
        tags
      }
    }

    Запит зі змінними

    Змінні роблять ваші запити динамічними та запобігають вразливостям введення:

    query GetBook($bookId: ID!) {
      book(id: $bookId) {
        title
        author {
          name
        }
      }
    }
    {
      "bookId": "1"
    }

    Приклад мутації

    mutation AddBook($input: CreateBookInput!) {
      createBook(input: $input) {
        id
        title
        author {
          name
        }
      }
    }
    {
      "input": {
        "title": "Designing Data-Intensive Applications",
        "authorId": "42",
        "publishedYear": 2017,
        "genre": "Technology"
      }
    }

    Фрагменти

    Фрагменти дозволяють вам повторно використовувати вибір полів у кількох запитах:

    fragment BookDetails on Book {
      id
      title
      genre
      publishedYear
    }
    
    query {
      books {
        ...BookDetails
        author {
          name
        }
      }
    }

    8. Оновлення в реальному часі за допомогою підписок {#subscriptions}

    Підписки GraphQL підтримують постійне з’єднання — зазвичай через WebSockets — та надсилають дані клієнтам, коли на сервері відбуваються конкретні події.

    Визначення схеми

    type Subscription {
      bookAdded: Book!
      bookUpdated(id: ID!): Book!
    }

    Реалізація сервера (Apollo Server з WebSockets)

    npm install graphql-ws ws @graphql-tools/schema
    import { createServer } from 'http';
    import { WebSocketServer } from 'ws';
    import { useServer } from 'graphql-ws/lib/use/ws';
    import { makeExecutableSchema } from '@graphql-tools/schema';
    import { PubSub } from 'graphql-subscriptions';
    
    const pubsub = new PubSub();
    
    const resolvers = {
      Mutation: {
        createBook: async (_, { input }, { db }) => {
          const book = await db.collection('books').insertOne(input);
          pubsub.publish('BOOK_ADDED', { bookAdded: book });
          return book;
        },
      },
      Subscription: {
        bookAdded: {
          subscribe: () => pubsub.asyncIterator(['BOOK_ADDED']),
        },
      },
    };
    
    const schema = makeExecutableSchema({ typeDefs, resolvers });
    const httpServer = createServer();
    const wsServer = new WebSocketServer({ server: httpServer, path: '/graphql' });
    useServer({ schema }, wsServer);

    Підписка клієнта

    subscription OnBookAdded {
      bookAdded {
        id
        title
        author {
          name
        }
      }
    }

    9. GraphQL Introspection та інструменти розробника {#introspection}

    Система introspection GraphQL — одна з найбільш дружелюбних до розробника особливостей. Запитуючи мета-поля __schema та __type, клієнти та інструменти можуть виявити повну структуру вашого API під час виконання.

    Приклад запиту Introspection

    {
      __schema {
        types {
          name
          kind
          description
        }
      }
    }

    Основні інструменти розробника

    ІнструментМета
    GraphiQLIDE у браузері для написання та тестування запитів
    Apollo StudioПовне управління API, моніторинг продуктивності, реєстр схем
    PostmanПідтримка запитів GraphQL з управлінням колекціями
    InsomniaЛегкий клієнт API з підтримкою GraphQL
    GraphQL Code GeneratorАвтоматично генерує типи TypeScript з вашої схеми
    Apollo Client DevToolsРозширення браузера для налагодження кешу Apollo Client

    > Примітка безпеки: Вимкніть introspection у виробничих середовищах, щоб уникнути розкриття схеми вашого API потенційним зловмисникам. Apollo Server робить це простим:

    >

    > “`javascript

    > new ApolloServer({ typeDefs, resolvers, introspection: false });

    > “`

    10. Розгортання GraphQL у виробництві {#deploying}

    Переміщення GraphQL API з розробки у виробництво вимагає ретельної уваги до інфраструктури, продуктивності та надійності.

    Вибір правильної інфраструктури хостингу

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

    VPS Hosting — це відмінна відправна точка для більшості GraphQL API. План VPS Hosting дає вам виділені ресурси, доступ root та свободу налаштовувати ваш Node.js runtime, зворотний проксі та менеджер процесів саме так, як вам потрібно. VPS плани AlexHost побудовані для навантажень, чутливих до продуктивності, та включають SSD сховище та високопропускну здатність.

    Dedicated Servers — правильний вибір, коли ваш GraphQL API обробляє великі обсяги запитів, складні навантаження підписок або служить шлюзом, який агрегує кілька мікросервісів. З Dedicated Server ви отримуєте виключний доступ до всіх ресурсів CPU, RAM та I/O — без шумних сусідів, без конкуренції за ресурси та сирої потужності для обробки тисяч одночасних WebSocket з’єднань для підписок.

    GPU Hosting варто розглянути, якщо ваш GraphQL API служить шаром інтерфейсу для висновків машинного навчання, конвеєрів обробки даних у реальному часі або функцій на основі AI. GPU Hosting від AlexHost надає вам ресурси NVIDIA GPU, дозволяючи вашому API доставляти обчислювально інтенсивні результати з низькою затримкою.

    Стек розгортання для виробництва

    Надійне розгортання для GraphQL API у виробництві зазвичай виглядає так:

    Client → CDN / Load Balancer → Nginx (Reverse Proxy) → Node.js (PM2) → Database
                                                          ↘ Redis (Caching / PubSub)

    Крок 1: Встановіть та налаштуйте Nginx як зворотний проксі

    server {
        listen 80;
        server_name api.yourdomain.com;
    
        location /graphql {
            proxy_pass http://localhost:4000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_cache_bypass $http_upgrade;
        }
    }

    Заголовки Upgrade та Connection критичні для підтримки WebSocket, яка живить підписки GraphQL.

    Крок 2: Керуйте вашим процесом Node.js за допомогою PM2

    npm install -g pm2
    pm2 start index.js --name graphql-api --instances max
    pm2 save
    pm2 startup

    ###PPT

    15%

    Збережіть 15% на всі хостинг-послуги

    Перевірте свої навички і отримайте Знижку на будь-який план хостингу

    Використовуй код:

    Skills
    Почати