Mobile API Best Practices: Building Backends That Scale
Mobile APIs face constraints that web APIs do not: unreliable connectivity, bandwidth sensitivity, app versions you cannot force-update, and platform-specific auth flows. This guide covers the practical patterns for building mobile backends that handle these realities. It is aimed at teams designing new mobile APIs or improving existing ones on Azure.
Why mobile APIs need different thinking
A web application and its API typically deploy together. If you change the API, you deploy the front end at the same time. The client and server are always in sync.
Mobile apps break this assumption.
You cannot force-update. Users control when (or whether) they update. At any given time, your backend serves requests from multiple app versions, some months old. Your API must handle this gracefully.
Connectivity is unreliable. Mobile devices switch between WiFi, 4G, and offline. APIs must support partial responses, retries, and offline-first workflows without creating duplicate data or inconsistent state.
Bandwidth costs money. On cellular networks, every unnecessary byte in a response costs the user (and increases latency). Payload optimisation is not a nice-to-have; it directly affects perceived performance and data usage.
Security is harder. The device is untrusted. You cannot store secrets on it. Auth flows, token storage, and certificate pinning all need mobile-specific approaches.
These constraints shape every decision from protocol choice to error handling.
Choosing the right protocol
REST: the practical default
REST is the right choice for most mobile backends. The reasons are pragmatic:
- Every mobile HTTP library (URLSession, Retrofit, Axios) handles REST natively
- HTTP caching with ETags and Cache-Control reduces bandwidth and improves offline experience
- OpenAPI specifications generate mobile client code automatically
- Debugging is straightforward (readable JSON payloads, standard HTTP status codes)
Use REST unless you have a specific reason to add a different protocol.
GraphQL: when data shapes vary
Consider GraphQL if your mobile app has screens that display data from many related entities and the data requirements change frequently. A social feed, a marketplace browse page, or a data-rich dashboard can benefit from GraphQL’s ability to fetch exactly the fields needed in a single request.
The trade-off is complexity: schema management, query cost control, and the loss of HTTP-level caching. For a full comparison, see our guide on REST vs GraphQL vs gRPC.
gRPC: for internal paths only
gRPC is excellent for internal service-to-service communication behind your mobile API layer. It is not practical as the mobile client’s primary protocol because of limited browser tooling and HTTP/2 requirements. Use it behind a REST or GraphQL BFF layer.
Authentication and token management
OAuth 2.0 with PKCE
The Authorization Code flow with PKCE (Proof Key for Code Exchange) is the standard for native mobile apps. It replaces the implicit flow, which is now considered insecure.
The flow:
- The app generates a random
code_verifierand derives acode_challenge - The app opens the system browser to the authorization server with the
code_challenge - The user authenticates and consents
- The authorization server redirects back to the app with an authorization code
- The app exchanges the code and
code_verifierfor tokens
PKCE is critical because native apps cannot securely store a client secret. The code_verifier proves that the app that initiated the flow is the same one redeeming the code.
Azure Entra ID and Azure AD B2C both support PKCE. Use MSAL (Microsoft Authentication Library) for the implementation.
Token storage
- iOS: Store tokens in the Keychain with
kSecAttrAccessibleWhenUnlockedThisDeviceOnly - Android: Store tokens in the Android Keystore with biometric binding where appropriate
- Never store tokens in shared preferences, local storage, or plain files
Token refresh
Access tokens should be short-lived (5-15 minutes). Refresh tokens should be longer-lived but rotated on use (rotation prevents replay attacks if a refresh token is compromised). Implement silent token refresh in the app’s HTTP interceptor so API calls do not fail when the access token expires.
Biometric authentication
Biometric auth (Face ID, fingerprint) provides a smoother user experience for token re-authentication. It does not replace OAuth. The pattern is: authenticate fully with PKCE on first login, store the refresh token in the platform keychain bound to biometric, and use biometric to unlock the refresh token for subsequent sessions.
Designing for unreliable connectivity
Idempotency keys
When a mobile app submits a write operation (place order, make payment, create booking) and loses connectivity, it will retry. Without idempotency, the retry creates a duplicate.
Require an Idempotency-Key header on all write operations. The server checks whether it has already processed a request with that key and returns the original response instead of processing again. Generate the key client-side (a UUID) and attach it to the request before the first attempt.
Optimistic updates with server reconciliation
For responsiveness, update the local UI immediately when the user takes an action. Queue the API call. When connectivity returns, send the request. If the server rejects it (conflict, validation failure), roll back the local state and notify the user.
This pattern requires the API to return clear, actionable error responses so the app can explain what happened and offer a resolution.
Delta sync
For list and feed endpoints, support delta synchronisation. The client sends its last-known sync token or timestamp. The server returns only items that have changed since then.
GET /api/v1/orders?since=2026-04-10T08:00:00Z
This reduces payload sizes dramatically for apps that sync frequently. Combine with ETags for cache validation on individual resource endpoints.
Retry strategy
Implement exponential backoff with jitter on the client:
- First retry: 1 second + random jitter (0-500ms)
- Second retry: 2 seconds + jitter
- Third retry: 4 seconds + jitter
- Maximum: 30 seconds between retries, then stop and notify the user
The server should return Retry-After headers on 429 (rate limited) and 503 (service unavailable) responses. The client should respect these.
Payload optimisation
Return only what the client needs
Design endpoints with mobile in mind. If the mobile app only needs a user’s name and avatar for a list view, provide a lightweight list endpoint rather than forcing the app to fetch full user profiles.
For REST APIs, use sparse fieldsets (?fields=name,avatar) or separate list and detail endpoints. For GraphQL, the client controls field selection natively.
Pagination
Always paginate list endpoints. Use cursor-based pagination rather than offset-based for mobile:
GET /api/v1/orders?limit=20&after=cursor_abc123
Cursor-based pagination is stable when items are added or removed between pages. Offset-based pagination (skip/take) breaks when the underlying data changes, causing duplicate or missing items.
Compression
Enable gzip or Brotli compression on all API responses. This is configured at the server or gateway level and reduces JSON payload sizes by 60-80%. Mobile clients handle decompression natively.
Image handling
Do not return full-resolution image URLs in API responses. Return image URLs with size parameters or use a CDN with on-the-fly resizing (Azure CDN with image optimisation, or Cloudflare Image Resizing). Let the mobile app request the resolution appropriate for its screen density.
Push notifications
Push notifications are a server-to-device channel that complements the request-response API.
Architecture
- Azure Notification Hubs for cross-platform push (APNs for iOS, FCM for Android) with tag-based targeting
- Backend trigger: the API or a background service publishes a notification when a relevant event occurs (order shipped, message received, appointment reminder)
- Deep linking: include a deep link URL in the notification payload so tapping the notification opens the correct screen in the app
Notification registration
The app registers its device token with the backend on launch and when the token refreshes. Associate the device token with the user account so notifications target the right user, not just the right device.
Handle the case where a user has multiple devices. Either send to all registered devices or let the user manage notification preferences per device.
For our implementation approach, see mobile push notifications.
Versioning for apps you cannot force-update
URL path versioning
Use major version numbers in the URL path: /v1/orders, /v2/orders. This is the clearest approach for mobile because the app hardcodes its API version and the server can route to the correct implementation.
Supporting multiple versions
Plan to support at least the current version and one prior version simultaneously. This gives users time to update. For each version:
- Maintain a separate set of request/response contracts (or a shared contract with version-specific transformations)
- Run integration tests against all supported versions
- Monitor traffic per version to know when adoption of a new version is high enough to deprecate the old one
Minimum version enforcement
Include a minimum supported API version in your app’s remote configuration (Firebase Remote Config, Azure App Configuration). When the app launches, check whether its version is still supported. If not, show an update prompt.
This lets you enforce updates for security patches or breaking changes without relying on app store review timelines.
Non-breaking changes
Add fields, endpoints, and optional parameters freely without bumping the major version. Never remove fields, change field types, or change the meaning of existing fields without a major version bump. Document your compatibility policy so mobile teams know what to expect.
For a deep dive on versioning approaches, see our guide on API versioning strategies.
Azure architecture for mobile backends
A production mobile backend on Azure typically includes:
- Azure API Management as the gateway: rate limiting, JWT validation, analytics, and version routing
- Azure App Service or Container Apps for the API (or BFF layer if you serve multiple client types)
- Azure SQL or Cosmos DB for data (SQL for relational, Cosmos for globally distributed or document-oriented workloads)
- Azure Redis Cache for session state, response caching, and rate limit counters
- Azure Entra ID or AD B2C for authentication
- Azure Notification Hubs for push notifications
- Azure Front Door for global distribution, WAF protection, and DDoS mitigation
- Application Insights for monitoring, distributed tracing, and alerting
For full details on our mobile backend architecture, see mobile app backends. For the CI/CD pipeline that deploys mobile apps alongside their backends, see mobile app CI/CD.
Frequently asked questions
Should I use REST or GraphQL for a mobile app backend?
How do I handle authentication for a mobile app?
How should I version a mobile API?
What is the best Azure architecture for a mobile backend?
How do I handle offline functionality in a mobile app?
How do I handle rate limiting for mobile clients?
Related guides
Planning an Integration Strategy: A Guide for Business and Technology Leaders
Integration projects fail when the strategy is wrong, not when the technology is wrong. A practical guide to planning an integration programme that moves at AI-augmented speed.
Backend for Frontend (BFF): API Patterns for Mobile and Web Clients
When to use the BFF pattern, how it compares to a single API or API gateway, and how to implement it on Azure. Practical architecture for teams serving mobile, web, and internal clients.
API Versioning Strategies That Actually Work at Scale
URL path, header, and query parameter versioning compared. Breaking vs non-breaking changes, deprecation workflows, Azure APIM versioning, and a practical policy template.