Mobile changes the API contract
A mobile client may be suspended mid-request, move between networks, remain several releases behind, and need to present useful state while offline. Those constraints should appear in the API contract rather than being left for client code to infer.
A phone on a cellular network deals with high latency, limited bandwidth, and a battery that drains with every network call. Users switch between WiFi and LTE mid-request. They walk into elevators. They close the app and reopen it thirty seconds later expecting the same state.
The best API review includes backend and mobile engineers together. They can agree on retry safety, compatibility, pagination, errors, caching, and observability before either side builds around an assumption.
Measure payload and round-trip cost
Payload size matters, but so do request count, latency, parsing cost, and cacheability. Record response sizes and timings on a constrained network profile and a representative device. Optimize the endpoints that affect user journeys instead of imposing one arbitrary size limit everywhere.
- Return only the fields the client needs. Avoid dumping entire database rows into responses.
- Use a
fieldsquery parameter so mobile clients can request sparse fieldsets. - Compress responses with gzip or Brotli. This is table stakes, but many APIs still skip it.
- Avoid deeply nested JSON. Flat structures are cheaper to parse on low-end devices.
Pagination done right: cursor-based, not offset
Offset-based pagination breaks on mobile. A user scrolls through a feed, the dataset shifts underneath them, and suddenly they see duplicate items or miss entries entirely. This is not a theoretical problem. It happens constantly in production.
Cursor-based pagination solves this. Instead of saying "give me page 5," the client says "give me the next 20 items after this cursor." The cursor is an opaque token that points to a stable position in the dataset.
- Cursors survive inserts and deletes in the underlying data.
- They work naturally with infinite scroll, which is the dominant mobile UI pattern.
- Include a
has_moreboolean in the response so clients know when to stop fetching.
A good pagination response includes three things: the data array, a next cursor, and a has_more flag. Nothing else is needed.
Error responses mobile clients can actually parse
Returning a raw 500 with an HTML error page is not helpful to a mobile client trying to show a toast message. Mobile apps need structured, predictable error responses they can parse programmatically and present to the user in context.
Every error response should follow the same shape. We use a simple structure across all our projects:
- A machine-readable
error_codestring likeINVALID_TOKENorRATE_LIMITED. - A developer-facing
messagethat helps diagnosis. User-facing copy should normally remain under client control for clarity and localization. - An optional
fieldkey for validation errors, so the client knows which input to highlight. - A consistent HTTP status code. Do not return 200 for errors. Ever.
Mobile engineers should never have to regex-match an error message to figure out what went wrong.
Versioning that does not break old app versions
Web apps deploy instantly. You push a change and every user gets it. Mobile apps do not work that way. Users on version 2.1 of your app might not update for months. Some never will. Your API has to support them all.
Path, header, and media-type versioning can all work. More important is a compatibility policy the team can test. Prefer additive changes within a version, observe which app versions still call an endpoint, and define how a breaking change is introduced and retired.
- Do not change the meaning or type of a field that released clients already consume.
- Publish deprecation and sunset signals, then choose the retirement window from observed client adoption and product risk.
- Reserve forced updates for cases that justify blocking the user, such as a security or incompatible-protocol event. Design a graceful minimum-version response before an emergency.
Caching headers and ETags for mobile
Caching can reduce latency and unnecessary transfers, but the policy depends on the resource. Public reference data, private account state, and a one-time command should not share the same cache rules.
With ETags, a mobile client can make a conditional request. If the data has not changed, the server returns a 304 Not Modified with no body. That saves bandwidth, battery, and time. For data that changes infrequently, like user profiles or app configuration, this is a massive win.
- Set
Cache-Control: max-agefor static or slow-changing resources. - Use
ETagandIf-None-Matchfor dynamic data that the client polls. - Return
Last-Modifiedheaders so clients can do conditional GETs.
GraphQL vs REST for mobile: a practical take
GraphQL lets mobile clients request exactly the data they need. No over-fetching. No under-fetching. In theory, it is perfect for mobile. In practice, it depends.
GraphQL shines when your mobile app has many screens with different data requirements hitting the same underlying models. Instead of building dozens of bespoke REST endpoints, you expose a graph and let the client query what it needs.
REST wins when your API surface is small and well-defined. It is simpler to cache at the HTTP layer. It is easier to monitor and rate-limit. The tooling is more mature.
Pick the tool that matches your team and your problem. Do not pick GraphQL because it is trendy. Do not avoid it because it is unfamiliar.
In our projects, we often use REST for straightforward CRUD operations and introduce GraphQL selectively for screens that aggregate data from multiple sources. Pragmatism beats purity.
Build APIs with mobile in mind from day one
Retrofitting mobile-friendliness into an existing API is painful and expensive. The best time to think about payload size, pagination, caching, and versioning is before you write the first endpoint.
If you are building a product that will have a mobile client, involve a mobile engineer in your API design review. They will catch problems that backend engineers simply do not think about.
Before implementation, write down the offline boundary, retry semantics, supported app versions, and error model. Those four decisions prevent more client-side workarounds than a fashionable transport choice.
Contact DEVSFLOW for a mobile-side review of an API contract or an existing failure trace.