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

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

Используйте код: Skills Начать
Рубрики
Выделенные

Полное руководство по GraphQL: создавайте более быстрые и умные API на высокопроизводительном хостинге

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

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

1. Что такое GraphQL?

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

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

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

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

2. GraphQL vs. REST: Почему это важно

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

ИзмерениеRESTGraphQL
EndpointsМножественные (один на ресурс)Единая унифицированная конечная точка
Получение данныхФиксированная структура ответаПоля, указанные клиентом
Over-fetchingРаспространеноИсключено по дизайну
Under-fetchingРаспространено (проблема N+1)Решено вложенными запросами
ВерсионированиеТребуется версионирование URL или заголовкаЭволюция схемы без версионирования
Поддержка реального времениТребует WebSockets или pollingВстроенные подписки
Система типовОпциональная (OpenAPI/Swagger)Встроенная, обязательная схема
КешированиеHTTP-уровневое кеширование простоеТребует кеширования с учетом запроса

GraphQL особенно мощен для:

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

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

3. Ключевые особенности GraphQL

3.1 Точная выборка данных

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

Это трансформирует работу команд, разрабатывающих продукты для нескольких платформ — веб, iOS, Android, Smart TV — где каждая поверхность имеет разные потребности в данных. Вместо того чтобы поддерживать отдельные REST конечные точки или принимать раздутые полезные нагрузки, каждый клиент отправляет адаптированный запрос и получает адаптированный ответ.

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

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

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

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

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

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

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

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

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

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

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

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

4. Настройка GraphQL сервера

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. Определение вашей схемы

Схема — это основа каждого 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

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

    Signature Resolver

    fieldName: (parent, args, context, info) => value
    • parent — разрешенное значение родительского типа (полезно для вложенных resolvers).
    • args — аргументы, переданные полю в запросе.
    • context — общий объект, передаваемый через всю цепь resolver, обычно содержащий аутентифицированного пользователя, подключение к базе данных или data loaders.
    • 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. Запрос и изменение данных

    Базовый запрос

    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. Обновления в реальном времени с подписками

    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 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 в production окружении, чтобы избежать раскрытия схемы вашего API потенциальным злоумышленникам. Apollo Server делает это простым:

    >

    > “`javascript

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

    > “`

    10. Развертывание GraphQL в Production

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

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

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

    VPS Hosting — отличная отправная точка для большинства GraphQL API. План VPS Hosting предоставляет вам выделенные ресурсы, root доступ и свободу настройки вашего Node.js runtime, reverse proxy и process manager точно так, как вам нужно. 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 доставлять вычислительно интенсивные результаты с низкой задержкой.

    Production стек развертывания

    Надежное production развертывание для GraphQL API обычно выглядит так:

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

    Шаг 1: Установка и настройка Nginx в качестве reverse proxy

    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. SSL Certificate от AlexHost гарантирует, что все данные в пути между клиентами и вашей GraphQL конечной точкой зашифрованы. Это особенно важно для API, которые обрабатывают токены аутентификации, личные данные или финансовую информацию.

    # Install Certbot and obtain a certificate
    sudo apt install certbot python3-certbot-nginx
    sudo certbot --nginx -d api.yourdomain.com

    Шаг 4: Регистрация вашего домена

    Вашему API нужен запоминающийся, профессиональный домен. Domain Registration через AlexHost дает вам доступ ко всем основным TLD с простым управлением DNS, что облегчает указание вашего домена на ваш сервер и настройку поддоменов для staging и production окружений.

    Стратегии кеширования

    Модель с единственной конечной точкой GraphQL означает, что кеширование на уровне HTTP (которое зависит от дифференциации URL) не работает из коробки. Используйте вместо этого эти стратегии:

    • Persisted Queries — клиенты отправляют хеш запроса вместо полной строки запроса, что позволяет кешировать CDN по хешу.
    • Response Caching — кешируйте результаты resolver в Redis на основе хеша запроса и переменных.
    • DataLoader — пакетируйте и кешируйте вызовы базы данных в рамках одного выполнения запроса.
    • Apollo Cache — нормализованный кеш на стороне клиента, который исключает избыточные сетевые запросы.

    11. Лучшие практики безопасности

    Гибкость GraphQL — это палка о двух концах. Без надлежащих защитных механизмов один вредоносный запрос может исчерпать ресурсы вашего сервера.

    Ограничение глубины запроса

    Предотвратите глубоко вложенные запросы от вызывания рекурсивных поисков в базе данных:

    import depthLimit from 'graphql-depth-limit';
    
    new ApolloServer({
      typeDefs,
      resolvers,
      validationRules: [depthLimit(7)],
    });

    Анализ сложности запроса

    Назначьте стоимость каждому полю и отклоняйте запросы, превышающие бюджет сложности:

    import { createComplexityLimitRule } from 'graphql-validation-complexity';
    
    new ApolloServer({
      validationRules: [createComplexityLimitRule(1000)],
    });

    Ограничение частоты запросов

    Применяйте ограничение частоты запросов на уровне Nginx или в вашем приложении, используя библиотеку express-rate-limit для предотвращения злоупотреблений.

    Аутентификация и авторизация

    Используйте функцию context для присоединения аутентифицированного пользователя к каждому запросу:

    const server = new ApolloServer({
      typeDefs,
      resolvers,
      context: async ({ req }) => {
        const token = req.headers.authorization?.split('Bearer ')[1];
        const user = token ? await verifyToken(token) : null;
        return { user, db, authorLoader };
      },
    });

    Затем обеспечьте авторизацию в резолверах:

    createBook: (_, { input }, { user }) => {
      if (!user || !user.roles.includes('EDITOR')) {
        throw new ForbiddenError('You do not have permission to create books.');
      }
      // proceed with creation
    },

    Отключение интроспекции в production

    new ApolloServer({
      introspection: process.env.NODE_ENV !== 'production',
    });

    Валидация входных данных

    Никогда не доверяйте входным данным от клиента. Валидируйте все аргументы мутаций, используя библиотеку joi или zod перед передачей данных в слой базы данных.

    12. Заключение

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

    Ключевые концепции, которые следует запомнить из этого руководства:

    • Проектирование на основе схемы создает более поддерживаемые, самодокументируемые API.
    • Resolvers — это мост между вашей схемой и источниками данных — держите их простыми и делегируйте тяжелую работу слоям сервисов или доступа к данным.
    • DataLoader необходим для production GraphQL API — никогда не позволяйте вложенным resolvers генерировать неограниченные запросы к базе данных.
    • Subscriptions открывают возможности реального времени без добавления отдельной инфраструктуры WebSocket.
    • Безопасность требует целенаправленного внимания — ограничения глубины, анализ сложности, аутентификация и отключение интроспекции в production являются обязательными для публичных API.
    • Инфраструктура имеет значение — хорошо написанный GraphQL API заслуживает хостинга, который сможет его поддержать.

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

    Начните разработку. Ваши клиенты ждут возможности запросить именно те данные, которые им нужны.