Что такое GraphQL и как он работает
Основы3 минРедакция Innovialabs
Коротко
GraphQL — язык запросов к API, где клиент сам описывает, какие поля ему нужны, и получает ровно их одним запросом через единый эндпоинт, а сервер описывает доступные данные строго типизированной схемой.
Что такое GraphQL
GraphQL — это язык запросов к API и среда выполнения на сервере. Его создали в Facebook для мобильных приложений и открыли в 2015 году.
Главная идея: клиент сам говорит, какие данные ему нужны. Вместо десятка эндпоинтов вроде /users/1, /users/1/orders есть один адрес (обычно /graphql), куда отправляется запрос с описанием нужной формы ответа. Сервер возвращает JSON ровно этой формы.
Какие проблемы он решает
- Over-fetching — эндпоинт отдаёт больше полей, чем нужно экрану. На мобильном интернете это лишний трафик.
- Under-fetching — для одного экрана приходится делать несколько запросов подряд: пользователь, потом его заказы, потом товары.
- Разные клиенты — разные потребности. Веб, iOS и Android хотят разные наборы полей, и бэкенду приходится плодить версии эндпоинтов.
- Нет единого контракта. В GraphQL схема обязательна и всегда описывает, что доступно.
Схема
Схема — это контракт между клиентом и сервером. Она пишется на SDL (Schema Definition Language):
type User {
id: ID!
name: String!
orders: [Order!]!
}
type Order {
id: ID!
total: Float!
status: String!
}
type Query {
user(id: ID!): User
}
type Mutation {
createOrder(userId: ID!, total: Float!): Order!
}
type Subscription {
orderStatusChanged(orderId: ID!): Order!
}
! означает, что поле не может быть null. [Order!]! — обязательный список обязательных элементов.
Запросы (Query)
Запрос на чтение данных. Клиент выбирает поля, включая вложенные:
query {
user(id: "1") {
name
orders {
id
total
}
}
}
Ответ повторяет форму запроса:
{
"data": {
"user": {
"name": "Алия",
"orders": [{ "id": "10", "total": 250000 }]
}
}
}
Пользователь и его заказы пришли за один запрос, без лишних полей.
Мутации (Mutation)
Мутации изменяют данные: создают, обновляют, удаляют. После изменения можно сразу запросить нужные поля результата:
mutation {
createOrder(userId: "1", total: 99000) {
id
status
}
}
Подписки (Subscription)
Подписки дают обновления в реальном времени. Клиент подписывается на событие, а сервер присылает данные, когда оно происходит. Обычно работают поверх WebSocket. Подходят для статусов заказов, уведомлений, чатов.
Резолверы
Резолвер — функция на сервере, которая знает, как получить значение конкретного поля. Схема говорит «что есть», резолверы — «откуда взять».
const resolvers = {
Query: {
user: (_, { id }) => db.users.findById(id),
},
User: {
orders: (user) => db.orders.findByUserId(user.id),
},
};
Сервер разбирает запрос, проверяет его по схеме и вызывает резолверы только для запрошенных полей. Данные могут приходить из базы, другого API или кэша — клиенту это не важно.
Что важно знать на практике
- Проблема N+1. Если запросить список из ста пользователей с заказами, наивный резолвер сделает сто отдельных запросов к базе. Решение — батчинг, например через библиотеку DataLoader.
- Кэширование сложнее. Все запросы идут через POST на один адрес, поэтому HTTP-кэш работает хуже, чем в REST. Используют клиентский кэш (Apollo Client, urql) и persisted queries.
- Защита от тяжёлых запросов. Нужно ограничивать глубину и сложность запросов, иначе клиент может запросить огромное вложенное дерево.
- Ошибки. Ответ часто приходит с HTTP 200, а ошибки лежат в поле
errors. Мониторинг надо настраивать с учётом этого. - Интроспекция. Схему можно запросить у сервера, на этом построены инструменты автодополнения и генерации типов.
Официальная документация: graphql.org.