GraphQL for Mobile Commerce
Mobile commerce demands fast, efficient API calls over potentially slow cellular connections. REST APIs return fixed data structures, often sending more data than the client needs or requiring multiple round-trips to assemble a single screen. GraphQL solves both problems by letting the client specify exactly which fields to fetch in a single request.
Magento 2 ships with a comprehensive GraphQL API covering catalog, cart, checkout, customer accounts, and CMS content. This guide covers how to build performant mobile applications against this API, with optimization patterns proven in Magento 2 production environments.
Optimized Query Patterns
Product Listing Page
Mobile screens display less data than desktop. Fetch only what the UI renders:
query CategoryProducts($id: String!, $page: Int!, $size: Int!) {
products(
filter: { category_uid: { eq: $id } }
pageSize: $size
currentPage: $page
sort: { position: ASC }
) {
total_count
items {
uid
sku
name
url_key
price_range {
minimum_price {
final_price { value currency }
discount { percent_off }
}
}
small_image {
url
label
}
review_count
rating_summary
}
page_info {
current_page
total_pages
}
}
}
This query returns approximately 200 bytes per product versus 2-3KB from the equivalent REST endpoint. For a 20-product listing page, that is a 90 percent reduction in payload size.
Product Detail Page
query ProductDetail($urlKey: String!) {
products(filter: { url_key: { eq: $urlKey } }) {
items {
uid
sku
name
description { html }
short_description { html }
price_range {
minimum_price {
regular_price { value currency }
final_price { value currency }
discount { amount_off percent_off }
}
}
media_gallery {
url
label
position
... on ProductVideo {
video_content { video_url video_title }
}
}
... on ConfigurableProduct {
configurable_options {
attribute_uid
label
values {
uid
label
swatch_data {
value
... on ImageSwatchData { thumbnail }
... on ColorSwatchData { value }
}
}
}
variants {
attributes {
uid
label
code
}
product {
uid
sku
price_range {
minimum_price {
final_price { value currency }
}
}
stock_status
}
}
}
related_products {
uid
name
url_key
small_image { url }
price_range {
minimum_price {
final_price { value currency }
}
}
}
}
}
}
Use GraphQL fragments to share field selections across queries and reduce duplication in your mobile codebase.
Cart Operations
Efficient Cart Management
mutation AddToCart($cartId: String!, $sku: String!, $qty: Float!) {
addProductsToCart(
cartId: $cartId
cartItems: [{ sku: $sku, quantity: $qty }]
) {
cart {
id
total_quantity
prices {
grand_total { value currency }
subtotal_excluding_tax { value currency }
applied_taxes { label amount { value } }
discounts { label amount { value } }
}
items {
uid
quantity
product {
name
sku
small_image { url }
}
prices {
row_total { value currency }
total_item_discount { value }
}
}
}
}
}
Return the full cart state after every mutation. This eliminates the need for a separate cart fetch, saving a round-trip on every cart interaction.
Authentication Flow
Customer Token Management
mutation CustomerLogin($email: String!, $password: String!) {
generateCustomerToken(email: $email, password: $password) {
token
}
}
// Mobile client token management
class AuthManager {
private token: string | null = null;
private refreshTimer: NodeJS.Timeout | null = null;
async login(email: string, password: string): Promise<void> {
const { data } = await this.client.mutate({
mutation: LOGIN_MUTATION,
variables: { email, password },
});
this.token = data.generateCustomerToken.token;
await SecureStore.setItemAsync("auth_token", this.token);
// Magento tokens expire after 1 hour by default
this.refreshTimer = setInterval(() => this.refreshToken(), 50 * 60 * 1000);
}
getHeaders(): Record<string, string> {
return this.token ? { Authorization: `Bearer ${this.token}` } : {};
}
}
Caching Strategy for Mobile
Apollo Client Configuration
import { ApolloClient, InMemoryCache } from "@apollo/client";
import { persistCache, AsyncStorageWrapper } from "apollo3-cache-persist";
import AsyncStorage from "@react-native-async-storage/async-storage";
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
products: {
keyArgs: ["filter", "sort"],
merge(existing, incoming, { args }) {
if (args?.currentPage === 1) return incoming;
return {
...incoming,
items: [...(existing?.items || []), ...incoming.items],
};
},
},
},
},
SimpleProduct: { keyFields: ["uid"] },
ConfigurableProduct: { keyFields: ["uid"] },
},
});
// Persist cache to device storage for offline access
await persistCache({
cache,
storage: new AsyncStorageWrapper(AsyncStorage),
maxSize: 10 * 1024 * 1024, // 10MB
});
Network-Aware Fetching
import NetInfo from "@react-native-community/netinfo";
async function fetchWithNetworkAwareness<T>(
query: DocumentNode,
variables: Record<string, unknown>
): Promise<T> {
const netState = await NetInfo.fetch();
if (!netState.isConnected) {
// Return cached data only
return client.readQuery({ query, variables });
}
const fetchPolicy = netState.type === "cellular" && !netState.isInternetReachable
? "cache-first"
: "cache-and-network";
const { data } = await client.query({ query, variables, fetchPolicy });
return data;
}
Performance Optimization
Server-Side Query Complexity Limits
Protect the Magento backend from expensive queries:
// app/etc/env.php
'graphql' => [
'query_complexity' => 300,
'query_depth' => 10,
],
Response Compression
Enable gzip compression on the Magento server for GraphQL responses:
# nginx.conf
location /graphql {
gzip on;
gzip_types application/json;
gzip_min_length 256;
proxy_pass http://magento_backend;
}
GraphQL JSON responses compress extremely well — typical 60-80 percent size reduction.
Image Optimization for Mobile
Request appropriately sized images using Magento's resize parameters:
query {
products(filter: { category_uid: { eq: "MjA=" } }) {
items {
small_image {
url # Returns full-size image
}
thumbnail {
url # Returns thumbnail-size image
}
}
}
}
On the mobile client, use the thumbnail URL for list views and the full image URL for detail views. Implement progressive image loading with blur placeholders for a smooth experience on slow connections.
Best Practices
- Use query batching to combine multiple small queries into a single HTTP request
- Implement infinite scroll pagination instead of loading all products at once
- Cache product catalog data aggressively — prices and stock can have shorter TTL
- Prefetch the next page of products while the user scrolls for seamless experience
- Monitor GraphQL query performance with custom server monitoring metrics
- Use persisted queries in production to reduce request payload size and improve security
- Test on real devices over throttled connections to validate performance
A well-optimized GraphQL integration delivers native-quality performance in mobile commerce applications while leveraging the full power of the Magento 2 commerce engine.
Need help with this?
Our team handles this kind of work daily. Let us take care of your infrastructure.
Related Articles
How to Boost Magento 2 Performance in a Few Easy Steps
Magento 2 delivers incredible flexibility for eCommerce, but without proper optimization it can become sluggish. This guide walks through ten proven DevOps strategies to dramatically speed up your store, from PHP upgrades and full-page caching to Varnish, Redis, CDN configuration, and ongoing code audits.
MagentoHow to Upgrade Magento 2 from 2.4.7 to 2.4.8
Keeping Magento current is critical for security, performance, and compatibility. This step-by-step guide walks developers through upgrading from Magento 2.4.7 to 2.4.8, covering system requirements, pre-upgrade checks, Git workflow, Composer commands, and post-upgrade validation.
MagentoHow to Completely Disable "Compare Products" in Magento 2
Magento's built-in Compare Products feature can add unnecessary clutter and slow down page loads. This guide shows you how to fully remove it using layout XML overrides, CSS rules, and a quick CLI deploy -- keeping your storefront clean and fast.