Skip to content
API and Integration

Mobile API Best Practices: Building Backends That Scale

13 min read Matt Hammond

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:

  1. The app generates a random code_verifier and derives a code_challenge
  2. The app opens the system browser to the authorization server with the code_challenge
  3. The user authenticates and consents
  4. The authorization server redirects back to the app with an authorization code
  5. The app exchanges the code and code_verifier for 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?
REST is the safe default for most mobile apps. It is well-supported by every HTTP library, works with standard caching, and is simple to reason about. GraphQL is worth evaluating if your mobile app displays data from many related entities in complex views (dashboards, feeds, nested lists) and you want to reduce round trips and payload sizes. See our guide on REST vs GraphQL vs gRPC for a full comparison.
How do I handle authentication for a mobile app?
Use OAuth 2.0 with PKCE (Proof Key for Code Exchange). This is the standard for native mobile apps because it does not require storing a client secret on the device. Azure Entra ID and Azure AD B2C both support PKCE flows. Store tokens in the platform keychain (iOS Keychain, Android Keystore), never in shared preferences or local storage.
How should I version a mobile API?
Use URL path versioning (e.g. /v1/users, /v2/users) for simplicity. Mobile apps cannot be force-updated, so you must support at least two major versions simultaneously. Include the minimum supported API version in your app's remote config so you can prompt users to update when you deprecate an old version.
What is the best Azure architecture for a mobile backend?
For most mobile apps: Azure App Service or Container Apps for the API, Azure SQL or Cosmos DB for data, Azure Redis Cache for session and response caching, Azure Entra ID or AD B2C for auth, Azure Notification Hubs or a custom push service for notifications, and Azure Front Door for global distribution and WAF protection. Azure API Management sits in front for rate limiting, analytics, and versioning.
How do I handle offline functionality in a mobile app?
Design the API for eventual consistency. Use ETags or last-modified timestamps so the app can sync only changes since its last successful request. Queue writes locally on the device and replay them when connectivity returns, with conflict resolution logic on the server. Idempotency keys on write operations prevent duplicate actions when retries occur.
How do I handle rate limiting for mobile clients?
Implement rate limiting at the API gateway layer (Azure APIM) per user or per device token, not per IP address. Mobile devices share IP addresses on cellular networks. Return 429 Too Many Requests with a Retry-After header so the app can back off gracefully. Client-side, implement exponential backoff with jitter on retries.

Ready to transform your software?

Let's talk about your project. Contact us for a free consultation and see how we can deliver a business-critical solution at startup speed.