Разработка GraphQL API для мобильного приложения
GraphQL меняет модель взаимодействия клиента с сервером: вместо фиксированных эндпоинтов клиент декларирует, какие именно поля ему нужны, и получает ровно их. Для мобильного приложения это особенно ценно — экран с карточкой товара запрашивает только name, price, thumbnail, не тягая description, reviews, inventory как при REST. На медленных соединениях разница заметна.
Когда GraphQL оправдан для мобильного
GraphQL добавляет сложность: нужна серверная реализация (resolver'ы, schema, DataLoader), клиентская библиотека и обучение команды. Оправданные сценарии:
- Разные клиенты (iOS, Android, Web) нужно обслуживать с одного API, и их требования к данным сильно расходятся
- Активно меняющийся UI — можно добавить поля в запрос без изменения сервера
- Вложенные данные с переменной глубиной (социальный граф, каталог с категориями)
Для CRUD с предсказуемой структурой данных REST проще. GraphQL не серебряная пуля.
Apollo Client на Android
Apollo Kotlin — де-факто стандарт. Генерирует типобезопасные классы запросов из .graphql файлов во время сборки.
# app/src/main/graphql/GetProduct.graphql
query GetProduct($id: ID!) {
product(id: $id) {
id
name
price
thumbnail {
url
width
height
}
}
}
// автогенерированный тип GetProductQuery.Data
val response = apolloClient.query(GetProductQuery(id = productId)).execute()
val product = response.data?.product
apolloClient настраивается один раз с HttpEngine, заголовками авторизации и кешом:
val apolloClient = ApolloClient.Builder()
.serverUrl("https://api.example.com/graphql")
.addHttpHeader("Authorization", "Bearer $token")
.normalizedCache(MemoryCacheFactory(maxSizeBytes = 10 * 1024 * 1024))
.build()
normalizedCache — нормализованный кеш по id поля. Запрос продукта из ленты и из детальной страницы возвращает один объект в памяти — обновление в одном месте автоматически отражается везде.
Apollo Client на iOS
Apollo iOS генерирует Swift-код аналогично Kotlin.
let client = ApolloClient(
networkTransport: RequestChainNetworkTransport(
interceptorProvider: DefaultInterceptorProvider(store: store),
endpointURL: URL(string: "https://api.example.com/graphql")!
),
store: store
)
client.fetch(query: GetProductQuery(id: productId)) { result in
switch result {
case .success(let response):
let product = response.data?.product
case .failure(let error):
print(error)
}
}
Subscriptions для real-time
GraphQL subscriptions — WebSocket-канал для обновлений в реальном времени. Идеально для чатов, live-обновлений цены, статусов заказа:
subscription OnOrderStatusChanged($orderId: ID!) {
orderStatusChanged(orderId: $orderId) {
status
updatedAt
}
}
Apollo Kotlin поддерживает subscriptions через WebSocketNetworkTransport:
apolloClient.subscription(OnOrderStatusChangedSubscription(orderId = id))
.toFlow()
.collect { response ->
val status = response.data?.orderStatusChanged?.status
}
Оптимизация: Persisted Queries
Каждый GraphQL запрос отправляет полный текст query в теле запроса — это накладные расходы. Automatic Persisted Queries (APQ): клиент отправляет SHA256 хеш запроса, сервер возвращает данные если знает этот хеш, иначе просит прислать полный текст. Apollo Client поддерживает APQ из коробки.
N+1 и DataLoader на сервере
Классическая проблема GraphQL resolver'ов: запрос 100 продуктов триггерит 100 отдельных SQL-запросов для загрузки категории каждого. Решение — DataLoader (batch + cache): все запросы категорий за одну итерацию батчатся в один SELECT ... WHERE id IN (...).
Без DataLoader GraphQL схема, которая хорошо работает на 10 объектах, деградирует на 1000 объектах. Это не клиентская проблема, но её нужно проектировать на этапе разработки API.
Обработка ошибок
GraphQL возвращает HTTP 200 даже при ошибках — ошибки в теле ответа:
{
"data": { "product": null },
"errors": [{ "message": "Product not found", "extensions": { "code": "NOT_FOUND" } }]
}
Клиент обязан проверять errors массив независимо от HTTP статуса. Apollo Client предоставляет GraphQLError список в response.errors.
Что входит в работу
Проектируем GraphQL схему под требования мобильного клиента, реализуем resolver'ы с DataLoader, настраиваем Apollo Client под платформы с кешем и subscriptions, интегрируем аутентификацию через HTTP заголовки и WebSocket connection params.
Срок: 2–4 недели в зависимости от объёма схемы и необходимости бэкенда.







