Mobile API Design: REST vs GraphQL for Mobile Applications | SoniNow Blog

Limited TimeLearn More

api designrestgraphqlmobileoffline

Mobile API Design: REST vs GraphQL for Mobile Applications

Published

2026-06-23

Read Time

5 mins

Mobile API Design: REST vs GraphQL for Mobile Applications

The Mobile API Problem

Mobile applications face constraints that web APIs do not. Unreliable networks, limited bandwidth, battery sensitivity, and the need to display content instantly from local storage. The API design decisions you make on day one become the architecture your team lives with for years.

REST and GraphQL both work. But they optimize for different problems, and the wrong choice creates technical debt that compounds with every feature. This guide focuses specifically on the mobile context — how each approach handles offline mode, optimistic updates, pagination, and payload size.

REST: Simple, Cachable, Predictable

REST endpoints return fixed data structures. This simplicity makes caching straightforward — each URL is a unique cache key. HTTP caching headers (Cache-Control, ETag, Last-Modified) work out of the box with platforms like OkHttp on Android and URLCache on iOS.

// Swift — URLSession with cache policy
let config = URLSessionConfiguration.default
config.requestCachePolicy = .returnCacheDataElseLoad
config.urlCache = URLCache(
    memoryCapacity: 20 * 1024 * 1024,   // 20 MB memory
    diskCapacity: 100 * 1024 * 1024,    // 100 MB disk
    diskPath: "api-cache"
)
let session = URLSession(configuration: config)

The trade-off is over-fetching and under-fetching. A post list endpoint returns 40 fields when you only need title and author. A post detail screen needs three separate requests: /posts/:id, /posts/:id/comments, and /users/:id. Each round trip on a slow mobile connection adds 200-800ms of latency.

REST is best for: content-heavy apps with stable data shapes, APIs consumed by multiple clients, and teams that want HTTP-level caching without additional infrastructure.

GraphQL: Precise, Typed, Single-Endpoint

GraphQL lets the client specify exactly what data it needs. A single request replaces multiple REST round trips. Apollo Client and Relay provide sophisticated normalized caches that update automatically when mutations return affected data.

# GraphQL — fetch post with author and comments in one round trip
query PostDetail($id: ID!) {
  post(id: $id) {
    id
    title
    body
    author { id name avatar }
    comments(limit: 20) {
      id
      text
      author { id name }
      createdAt
    }
  }
}
// React Native — Apollo Client usage
const { data, loading, error } = useQuery(GET_POST_DETAIL, {
  variables: { id: postId },
  fetchPolicy: 'cache-first',
});

The normalized cache is GraphQL's killer feature for mobile. When a comment mutation returns the updated comment, Apollo automatically merges it into the cache and all active queries that reference that comment update. No manual cache invalidation.

The cost: a GraphQL server layer, more complex error handling, and N+1 query prevention at the resolver level.

Offline Strategy Comparison

REST offline approach:

  • Cache raw HTTP responses to disk
  • Use returnCacheDataElseLoad policy
  • Queue failed mutations in a local database
  • Manually invalidate cached responses when mutations succeed
// Kotlin — Room-backed REST cache
@Entity
data class CachedResponse(
    @PrimaryKey val url: String,
    val responseBody: String,
    val lastModified: String?,
    val expiresAt: Long
)

@Dao
interface CacheDao {
    @Query("SELECT * FROM cached_responses WHERE url = :url")
    suspend fun getCached(url: String): CachedResponse?
}

GraphQL offline approach:

  • Apollo normalized cache persists to AsyncStorage (React Native) or SQLite (native)
  • Automatic cache reconciliation on mutation success
  • Optimistic updates rendered instantly, rolled back on error
  • Field-level granularity — only refetch stale portions
// React Native — Apollo optimistic update
const [updatePost] = useMutation(UPDATE_POST, {
  optimisticResponse: {
    updatePost: {
      __typename: 'Post',
      id: postId,
      title: newTitle,
      likes: currentLikes + 1,
    },
  },
  update: (cache, { data }) => {
    cache.modify({
      id: cache.identify({ __typename: 'Post', id: postId }),
      fields: {
        likes: () => data.updatePost.likes,
      },
    });
  },
});

Pagination and List Performance

Mobile lists need infinite scroll with minimal payloads. REST handles this cleanly with cursor-based pagination:

GET /api/posts?cursor=abc123&limit=20
Link: <https://api.example.com/posts?cursor=def456&limit=20>; rel="next"

GraphQL's Relay connection spec standardizes cursor pagination with hasNextPage and edges.node patterns. The cache knows how to merge new pages without duplicates — something REST requires manual handling for.

query Feed($first: Int!, $after: String) {
  feed(first: $first, after: $after) {
    edges { node { id title } cursor }
    pageInfo { hasNextPage endCursor }
  }
}

When to Use Which

| Factor | REST | GraphQL | |---|---|---| | Network efficiency | Over-fetches by default | Precise payloads | | Caching | HTTP cache headers, simple | Normalized cache, powerful but complex | | Offline support | Manual, database-backed | Built-in with Apollo/Relay | | Tooling maturity | Mature, universal | Mature, mobile-specific | | Learning curve | Low | Medium-high | | Server complexity | Low | Medium (resolvers, batching) | | Best for | Simple CRUD, content apps | Complex data graphs, social feeds |

The Pragmatic Approach

Many production mobile apps use both. REST for simple resources (images, static content, file uploads) and GraphQL for the primary data graph (feeds, profiles, social features). This hybrid approach gives you HTTP caching for assets and normalized caching for relational data.

At [SoniNow], we design mobile-first API layers that handle offline connectivity, optimistic updates, and efficient data transfer — regardless of protocol choice.

Learn about our mobile development services →

Building a mobile app? Let's design your API layer together.