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. Реализация резолверов
  7. Запрос и изменение данных
  8. Обновления в реальном времени с подписками
  9. GraphQL интроспекция и инструменты разработчика
  10. Развертывание GraphQL в production
  11. Лучшие практики безопасности
  12. Заключение

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

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

Этот подход устраняет две наиболее стойкие проблемы в проектировании REST API:

  • Over-fetching — получение гораздо большего объема данных, чем клиенту действительно нужно, что приводит к потере пропускной способности и времени обработки.
  • Under-fetching — получение слишком мало данных в одном запросе, что заставляет клиента делать несколько последующих вызовов для получения полной картины.

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

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

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

ИзмерениеRESTGraphQL
Конечные точкиМножественные (одна на ресурс)Единая унифицированная конечная точка
Получение данныхФиксированная структура ответаПоля, указанные клиентом
Over-fetchingРаспространеноИсключено по дизайну
Under-fetchingРаспространено (проблема N+1)Решено с помощью вложенных запросов
ВерсионированиеТребуется версионирование URL или заголовкаЭволюция схемы без версионирования
Поддержка реального времениТребует WebSockets или опросаВстроенные подписки
Система типовОпциональная (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). Схема является авторитетным контрактом между сервером и каждым клиентом, который его использует. Она определяет:

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

Поскольку схема строго типизирована, целые категории ошибок перехватываются во время разработки, а не в production. Инструменты могут проверять запросы против схемы до их выполнения, а IDE могут предоставлять точное автодополнение и встроенную документацию.

3.3 Обновления в реальном времени с подписками

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

Это делает GraphQL естественным выбором для:

  • Приложений для чата и обмена сообщениями в реальном времени
  • Инструментов совместного редактирования
  • Финансовых информационных панелей с данными живого рынка
  • Уведомлений в реальном времени и лент активности
  • Синхронизации состояния многопользовательской игры

3.4 Интроспекция

GraphQL API самодокументируются по дизайну. Система интроспекции позволяет клиентам запрашивать саму схему — обнаруживая доступные типы, поля, запросы, мутации и их описания во время выполнения. Эта возможность питает инструменты разработчика, такие как GraphiQL и Apollo Studio, которые предоставляют интерактивные обозреватели API, конструкторы запросов и автоматическое создание документации без каких-либо дополнительных усилий со стороны автора API.

3.5 Эволюция схемы без версионирования

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

4. Настройка GraphQL сервера {#setting-up}

GraphQL не зависит от языка. Зрелые библиотеки серверов существуют по всему технологическому стеку. Вот наиболее широко используемые варианты:

Язык / RuntimeБиблиотека / Framework
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. Реализация резолверов {#implementing-resolvers}

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

    Сигнатура резолвера

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

    Пример: Резолверы с базой данных

    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

    Вложенные резолверы могут вызвать проблему 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}

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

    Пример запроса интроспекции

    {
      __schema {
        types {
          name
          kind
          description
        }
      }
    }

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

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

    > Примечание безопасности: Отключите интроспекцию в production окружениях, чтобы избежать раскрытия вашей схемы API потенциальным злоумышленникам. Apollo Server делает это простым:

    >

    > “`javascript

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

    > “`

    10. Развертывание GraphQL в production {#deploying}

    Перемещение GraphQL API из разработки в production требует тщательного внимания к инфраструктуре, производительности и надежности.

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

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

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

    Выделенные серверы — правильный выбор, когда ваш GraphQL API обрабатывает большие объемы запросов, сложные рабочие нагрузки подписок или служит шлюзом, агрегирующим несколько микросервисов. С выделенным сервером вы получаете исключительный доступ ко всем ресурсам CPU, RAM и I/O — без шумных соседей, без конкуренции за ресурсы и необработанной мощности для обработки тысяч одновременных WebSocket соединений для подписок.

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

    Стек развертывания production

    Надежное развертывание production для 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

    --instances max включает режим кластера, порождая один рабочий процесс на ядро CPU для максимизации пропускной способности.

    Шаг 3: Защитите с помощью SSL

    Каждый production API должен обслуживаться через HTTPS.

    15%

    Сэкономьте 15% на всех хостинговых услугах

    Проверьте свои навыки и получите скидку на любой тарифный план

    Используйте код:

    Skills
    Начать