GraphQL: Query и Mutation

Query и Mutation — два основных типа операций GraphQL: Query читает данные (аналог GET), Mutation изменяет данные (аналог POST/PUT/DELETE).

Зачем нужно

Явное разделение на Query и Mutation задаёт намерение операции на уровне языка, позволяет серверу применять разные политики кэширования, авторизации и оптимизации. Понимание синтаксиса позволяет писать корректные запросы и мутации с переменными.

Где используется

  • Клиентские приложения с Apollo Client, urql, React Query
  • Серверные GraphQL resolver-функции на Node.js (Apollo Server, graphql-yoga)
  • GraphQL Playground / GraphiQL для ручного тестирования API

Query — чтение данных

# Именованный запрос с переменной
query GetUser($id: ID!) {
  user(id: $id) {
    id
    name
    email
    posts {
      title
      createdAt
    }
  }
}

# Анонимный запрос (можно, но не рекомендуется в продакшне)
{
  users {
    id
    name
  }
}

# Алиасы — два поля с одним именем в разных вызовах
query TwoUsers {
  alice: user(id: 1) { name }
  bob:   user(id: 2) { name }
}

# Фрагменты — переиспользование набора полей
fragment UserFields on User {
  id
  name
  email
}

query GetUsers {
  activeUsers: users(status: ACTIVE) { ...UserFields }
  admins: users(role: ADMIN)         { ...UserFields }
}

Mutation — изменение данных

# Создание
mutation CreateUser($input: CreateUserInput!) {
  createUser(input: $input) {
    id
    name
    email
    createdAt
  }
}

# Обновление
mutation UpdateUser($id: ID!, $name: String!) {
  updateUser(id: $id, name: $name) {
    id
    name
  }
}

# Удаление
mutation DeleteUser($id: ID!) {
  deleteUser(id: $id) {
    success
    message
  }
}

Выполнение через fetch

async function graphqlRequest(query, variables = {}) {
  const res = await fetch('/graphql', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ query, variables }),
  });
  const { data, errors } = await res.json();
  if (errors?.length) throw new Error(errors[0].message);
  return data;
}

// Query
const { user } = await graphqlRequest(
  `query GetUser($id: ID!) { user(id: $id) { id name } }`,
  { id: '42' }
);

// Mutation
const { createUser } = await graphqlRequest(
  `mutation CreateUser($input: CreateUserInput!) {
     createUser(input: $input) { id name }
   }`,
  { input: { name: 'Антон', email: 'anton@example.com' } }
);

Частые ошибки

  • Использование Query для операций изменения данных — нарушает семантику и кэширование
  • Не передают переменные — конкатенируют значения в строку (уязвимость инъекций)
  • Не проверяют поле errors в ответе — HTTP-статус всегда 200
  • Запрашивают все поля (включая тяжёлые связи) без необходимости — N+1 проблема

Связанные темы

Ресурсы