Web & Mobile Development

REST API Design Mistakes That Break Your Mobile App

The Debuggers Engineering Team
11 min read

TL;DR

  • Inconsistent error response formats are the top cause of mobile app crashes from API calls. Define a single error schema and enforce it
  • Always version your API from day one. Breaking changes without versioning force emergency app updates
  • Use cursor-based pagination for mobile. Offset pagination breaks when new items are inserted during browsing
  • A 2MB JSON response on a 3G connection takes 8+ seconds. Design endpoints for mobile viewport data needs

Table of Contents

API design architecture diagram showing backend and mobile communication

Inconsistent Error Response Formats

This is the most common API design mistake because it causes silent app crashes instead of handled errors.

The problem: Different endpoints return errors in different shapes:

// Endpoint A returns:
{ "error": "User not found" }

// Endpoint B returns:
{ "message": "Invalid input", "code": "VALIDATION_ERROR" }

// Endpoint C returns:
{ "errors": [{ "field": "email", "msg": "already exists" }] }

// Endpoint D returns plain text:
"Internal server error"

The mobile app's error handling code cannot parse all four formats without specific handling for each endpoint. When a new error format appears in an untested code path, the JSON parsing fails and the app crashes.

The fix - one error schema for all endpoints:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The request contained invalid fields",
    "details": [
      {
        "field": "email",
        "message": "Email address is already registered",
        "code": "DUPLICATE_VALUE"
      }
    ],
    "requestId": "req_abc123"
  }
}

Use a middleware or error handler that wraps all errors into this format. Never return raw exception messages or stack traces in production.

Test your API error responses with our free API Request Tester by deliberately sending malformed data, missing authentication, and invalid parameters. Verify every endpoint returns the standard error format.

No API Versioning Strategy

You ship version 1.0 of your mobile app to 100,000 users. Three months later, the backend team changes the /users response to rename fullName to name. Every user on app version 1.0 now sees a blank name field (or crashes, depending on how the app handles missing fields).

Versioning approaches:

URL versioning (recommended for mobile):

GET /api/v1/users/123
GET /api/v2/users/123

Clear, easy to route, easy to monitor. The mobile app pins to a specific version and migrates in a new app release.

Header versioning:

GET /api/users/123
Accept: application/vnd.yourapp.v2+json

Cleaner URLs but harder to test (you need tools that set custom headers) and harder to monitor in access logs.

The rule: Support at least the current version (v2) and the previous version (v1) simultaneously. Give users 6-12 months to update before deprecating an old version.

API versioning strategy diagram showing concurrent version support

Pagination Design for Mobile

Offset pagination breaks on mobile. If a user is browsing page 2 (items 21-40) and a new item is inserted at the top, the items shift. When the user loads page 3, they see item 40 again because it moved from position 40 to position 41.

Cursor-based pagination fixes this:

// Request
GET /api/posts?limit=20&after=cursor_abc123

// Response
{
  "data": [...],
  "pagination": {
    "hasMore": true,
    "nextCursor": "cursor_def456",
    "prevCursor": "cursor_abc123"
  }
}

The cursor is typically an encoded representation of the last item's sort value (timestamp, ID, composite key). New items inserted above the cursor do not affect the next page's results.

When to use offset pagination: Admin dashboards, backoffice tools, and any interface where users jump to page 50 directly. Offset supports arbitrary page access; cursors only support sequential access.

Over-Fetching and Under-Fetching

Over-fetching: The /users/123 endpoint returns the user's profile, address, preferences, order history, payment methods, and notification settings in one response. The mobile app only needed the name and avatar for a list view. Result: wasted bandwidth, slow load times on mobile networks.

Under-fetching: The mobile app needs to display a restaurant card with the restaurant name, rating, and the first menu item's name. The API requires three separate calls: /restaurants/1, /restaurants/1/rating, /restaurants/1/menu. Result: three network roundtrips instead of one.

Solutions:

  1. Field selection (sparse fieldsets):
GET /api/restaurants/1?fields=name,rating,coverImage
  1. Endpoint design for views: Create endpoints that match mobile screen data needs:
GET /api/restaurants/1/card-summary
  1. GraphQL as an alternative: Let the client specify exactly what data it needs. This is a bigger architectural decision but eliminates both over-fetching and under-fetching permanently.

Authentication Token Handling

401 vs 403 Distinction

The mobile app needs to know the difference:

  • 401 Unauthorized: Token is invalid or expired. Action: attempt a token refresh. If refresh fails, redirect to login.
  • 403 Forbidden: Token is valid but the user does not have permission. Action: show an error message. Do NOT redirect to login because the user is authenticated, just not authorised.

Many APIs return 403 for both cases, which causes mobile apps to either loop on refresh attempts or unnecessarily force re-login.

Silent Token Refresh

The mobile app should handle token refresh transparently. When any API call returns 401:

  1. Pause the original request
  2. Call the refresh token endpoint
  3. If successful, retry the original request with the new token
  4. If refresh fails, redirect to login

Implement this as an HTTP interceptor (Dio interceptor in Flutter, Axios interceptor in React Native) so every API call is automatically protected.

Use our JWT debugger to inspect access and refresh tokens during development, verifying that expiry times, scopes, and claims are set correctly.

Missing Idempotency Keys

The user taps "Place Order" on their phone. The request takes 3 seconds on a slow connection. The user taps again because they think it did not work. Two orders are placed.

The fix - idempotency keys:

POST /api/orders
Idempotency-Key: order_7f3a8c2b-unique-uuid
Content-Type: application/json

{ "items": [...], "paymentMethod": "card_123" }

The server stores the idempotency key with the response. If the same key is sent again, the server returns the original response without executing the operation again.

Generate the idempotency key on the client side when the user initiates the action (button tap), not when the request is sent. This ensures retries use the same key.

Date and Timezone Handling

The only acceptable date format in APIs is ISO 8601:

{
  "createdAt": "2026-02-20T14:30:00Z",
  "deliveryTime": "2026-02-20T15:45:00+05:30",
  "dateOfBirth": "1990-06-15"
}

Common mistakes:

  • Unix timestamps (1740060600): ambiguous about precision (seconds vs milliseconds), unreadable in logs
  • Non-standard formats ("20/02/2026"): locale-dependent, parsing nightmare
  • Missing timezone ("2026-02-20 14:30:00"): is this UTC? Server local time? User local time?

Always store and transmit dates in UTC. Let the mobile client convert to local time for display. Include timezone offset when the original timezone matters (meeting times, delivery windows).

Mobile app displaying formatted API response data on screen

Response Size and Mobile Performance

A response size analysis for typical mobile contexts:

Response Size4G (20 Mbps)3G (2 Mbps)Poor Signal (500 Kbps)
50 KB20ms200ms800ms
200 KB80ms800ms3.2s
1 MB400ms4s16s
5 MB2s20s80s

A 2MB JSON response that works fine during development on WiFi becomes an 8-second wait on 3G. Users with poor connectivity will see loading spinners and eventually give up.

Optimisation strategies:

  • Enable gzip/brotli compression on your server (reduces JSON size by 70-90%)
  • Use pagination for lists (never return unbounded arrays)
  • Implement sparse fieldsets for endpoints that return large objects
  • Consider binary formats (Protocol Buffers, MessagePack) for high-frequency endpoints
  • Cache responses locally and implement stale-while-revalidate patterns

For validating your API response payloads and checking their size, use our JSON formatter and our SQL formatter for optimising the backend queries that generate these responses.

The Debuggers provides API design and backend development services, building mobile-optimised APIs for Flutter and React Native applications.

For more on mobile development choices, see our Flutter vs React Native 2026 comparison.

Frequently Asked Questions

Should I use REST or GraphQL for my mobile app API?

Use REST if your API serves data in predictable, well-defined shapes and your mobile views closely match your backend resources. Use GraphQL if your mobile app has many different views that each need different subsets of the same data, or if over-fetching is a significant performance concern. REST is simpler to implement, cache, and monitor. GraphQL is more flexible but adds complexity in schema design, caching, and error handling.

How do I handle breaking API changes without losing users?

Version your API from day one. When a breaking change is needed, release it under a new version (v2) while keeping the old version (v1) running. Update your mobile app to use v2 and publish the update. Monitor the percentage of users still on the old app version. Deprecate v1 only when 95%+ of active users have updated, or after a minimum of 6 months. Send in-app messages encouraging users on old versions to update.

What is the right page size for mobile API pagination?

20 items per page is a good default for most mobile list views. This balances response size (small enough for fast loads on slow connections), scroll depth (enough items to fill the screen with room to scroll), and API load (not too many requests for a full browse session). Adjust based on item size. If each item is large (contains images, long text), reduce to 10. If items are compact (just a name and status), you can increase to 50.

How do I test my API for mobile-specific issues?

Test with network throttling enabled. Chrome DevTools and Postman both support simulating 3G and poor network conditions. Test with empty responses, null fields, and fields that change type between requests (number vs string). Test with large response payloads to identify endpoints that need pagination or field selection. Our API Request Tester at thedebuggersitsolutions.com lets you run these tests directly from your browser.


Test your API endpoints now

Use our free API Request Tester to send GET, POST, PUT, and DELETE requests to your backend. Inspect response headers, status codes, and JSON payloads to verify your API follows mobile-friendly patterns.

Building a mobile-optimised API? The Debuggers designs and builds backend APIs specifically for mobile applications.

Need Help Implementing This in a Real Project?

Our team supports end-to-end development for web and mobile software, from architecture to launch.

rest api design mistakesapi design best practicesrest api mobileapi versioningrest api errors

Found this helpful?

Join thousands of developers using our tools to write better code, faster.