GraphQL — What It Is, Query Language and Application in Mobile Projects

Author: IT Sectr Published: 2026-03-06 Reading time: 9 min

GraphQL — is a query language for APIs and a runtime for executing those queries, developed by Facebook in 2012 and open-sourced in 2015. Unlike REST, where the server determines the response structure, GraphQL allows the client to specify exactly what data it needs, completely eliminating the problems of overfetching and underfetching. According to the State of JavaScript Survey (2025), 35% of surveyed developers use GraphQL, and among large companies it has been adopted by GitHub, Shopify, Airbnb, and The New York Times. GraphQL supports three types of operations: query (reading), mutation (writing), and subscription (real-time updates via WebSocket).

Key Takeaways

  • GraphQL — a query language where the client specifies the response structure
  • Solves overfetching (excess data) and underfetching (insufficient data) problems
  • Supports query, mutation, and subscription for different types of operations
  • Uses a single endpoint (usually /graphql) instead of multiple URLs like in REST
  • Based on a type system with a strict schema: all possible data is described in advance

What is GraphQL?

GraphQL — is a specification and runtime for APIs that gives the client full control over the data it receives. Developed by Facebook engineers to solve the problems of the News Feed mobile application, the specification was published as an open standard in 2015. Since 2018, GraphQL has been managed by the GraphQL Foundation with support from the Linux Foundation and companies such as Apollo, AWS, GitHub, SAP, and others.

Unlike REST, where each endpoint returns a fixed data structure, GraphQL uses a single endpoint that accepts a query string. The client describes in the query which fields it needs, and the server returns exactly those. For example, the query { user(id: "1") { name email } } will return only the user’s name and email, without extra fields like address, phone, or createdAt that would have to be fetched in REST.

GraphQL is not tied to any specific database or language. The specification only defines the query and response format. There are server implementations on Node.js (graphql-js, Apollo Server), Kotlin (graphql-kotlin, Netflix DGS Framework), Python (Graphene, Strawberry), Ruby (graphql-ruby), and other languages. Client libraries are available for all major platforms, including Apollo Client for iOS, Android, and web.

How GraphQL Works

The GraphQL architecture consists of three key components: Schema, Resolvers, and the GraphQL Engine. The schema defines what data types are available, what queries can be executed, and what arguments they accept. Resolvers are server-side functions that return data for each schema field. The engine receives the incoming query, validates it against the schema, calls the appropriate resolvers, and assembles the response.

The query processing flow looks like this:

  • The client sends a POST request to /graphql with a JSON body { "query": "..." }
  • The server parses the query, builds an AST (Abstract Syntax Tree), and validates it against the schema
  • The engine traverses the AST, calling resolvers for each field, collecting data
  • The response is returned in JSON format, strictly matching the query structure

The key advantage of the GraphQL architecture is field-level resolution. In REST, the developer either gets all fields of a resource (possibly with extras) or resorts to extensions like ?fields=name,email. In GraphQL, such filtering is built into the language: each query explicitly specifies which fields are needed, and the server returns exactly those. This is especially important for mobile applications, where the amount of data transferred directly affects loading speed and data usage.

Query, Mutation, and Subscription

GraphQL defines three types of operations, each corresponding to a specific interaction scenario. Query — for reading data, analogous to GET in REST. Mutation — for modifying data (create, update, delete), analogous to POST/PUT/DELETE. Subscription — for real-time updates via WebSocket, which has no direct analogy in classic REST (requires additional solutions like WebSocket or Server-Sent Events).

The basic query syntax is intuitive:

js
// Simple query with argument
query {
    user(id: "42") {
        name
        email
        avatarUrl
    }
}

// Mutation returning modified data
mutation {
    updateProfile(name: "Ivan") {
        id
        name
        updatedAt
    }
}

// Subscription — listens for real-time updates
subscription {
    newMessage(chatId: "chat_1") {
        id
        text
        sender { name }
    }
}

Query is executed in parallel — all fields at the same level are loaded simultaneously. This allows loading related data (user and their posts) in a single request without multiple round-trips. Mutation is executed sequentially — mutations in one request are executed one after another in declaration order. Subscription establishes a persistent connection via WebSocket, over which the server sends data when an event occurs.

Operations can accept variables to separate data from the query, directives (@include, @skip) for conditional field inclusion, and fragments for reusing sets of fields. These capabilities make GraphQL queries flexible and reusable, which is especially important in large projects with many screens and components.

GraphQL Schema and Type System

At the core of GraphQL lies a type system describing all possible data and API operations. The schema is a description of the types the server can return and the queries it accepts. The schema is written in Schema Definition Language (SDL) and serves as a contract between the client and server. The client can obtain the schema through introspection — a special query __schema that returns a complete description of the API.

Example schema for a blog:

js
// SDL — Schema Definition Language
type User {
    id: ID!
    name: String!
    email: String
    posts: [Post!]!
}

type Post {
    id: ID!
    title: String!
    content: String
    author: User!
}

type Query {
    user(id: ID!): User
    posts(page: Int): [Post!]!
}

The exclamation mark (!) denotes a non-null field — it is guaranteed to be present in the response. Square brackets [ ] denote a list. GraphQL supports scalar types (Int, Float, String, Boolean, ID), object types, enum, union, interface, and input types (for mutation arguments). Strong typing self-documents the API and allows client tools to generate code: TypeScript types, Kotlin data classes, Swift structures.

Introspection is a unique GraphQL feature absent in REST. The client can send a query to the schema and get a complete description of all types, fields, arguments, and directives. This underlies tools like GraphiQL and Apollo Studio, which automatically generate documentation and autocompletion for developers. Introspection also allows writing automated tests that verify schema compliance with the expected structure.

GraphQL vs REST

The choice between GraphQL and REST is one of the key architectural decisions when designing an API. Both approaches have their strengths and weaknesses, and the choice depends on the specific project requirements. REST wins in simplicity and universality, GraphQL in flexibility and query efficiency. Let’s look at the comparison table.

CriterionRESTGraphQL
Response structureFixed, server-definedFlexible, client-defined
OverfetchingOften — server returns all fieldsNo — client requests only needed fields
Number of requestsMultiple round-tripsSingle request for all data
CachingNative HTTP cachingRequires manual configuration
TypingNot built-in (depends on format)Strict, via SDL schema
Toolscurl, Postman, SwaggerGraphiQL, Apollo Studio, Introspection
File uploadNative via multipartRequires additional protocols
PerformancePredictable, easier to optimizeDepends on nested query complexity

The main drawback of GraphQL is caching complexity. In REST, HTTP caching works at the URL level: one request to /api/users/42 always returns the same structure, and the response can be cached by URL. In GraphQL, all requests go to a single endpoint, and the response structure depends on the request body. To solve this, Apollo Client uses a normalized cache on the client side, which breaks responses into individual entities by id and automatically updates them when new data is received.

Another important aspect is the N+1 problem. When requesting nested data (e.g., user posts and comments for each post), GraphQL may execute a separate SQL query for each list item. This is solved using DataLoader — a utility for batching and caching database queries, which groups individual requests into one batch. In REST, this problem is less pronounced since the developer controls the response structure on the server side.

GraphQL Query Examples

Let’s look at practical examples of using GraphQL in a Kotlin mobile application with Apollo Client. The examples demonstrate typical scenarios: loading data for a profile screen (query), creating a new post (mutation), and subscribing to new comments (subscription). Each example includes both the GraphQL query and the client-side code.

Query: Loading a profile with posts

A single GraphQL query loads the user, their latest posts, and the total number of followers. In REST, this would require at least 2-3 requests: /users/42, /users/42/posts, /users/42/stats. GraphQL combines them into a single round-trip, reducing screen load time on slow connections.

kotlin
// GraphQL query (in .graphql file)
query ProfileScreen($userId: ID!) {
    user(id: $userId) {
        name
        bio
        avatarUrl
        posts(limit: 10) {
            id
            title
            createdAt
        }
        followersCount
        followingCount
    }
}

// Client call (Apollo Client + Kotlin)
val response = apolloClient
    .query(ProfileScreenQuery(userId = "42"))
    .execute()
binding.nameText.text = response.data?.user?.name

Mutation: Creating a new post

The mutation not only creates a resource but also returns its current data for UI updates. The __typename field is used by Apollo Client for cache normalization — the client will automatically update the Post record in the cache upon a successful mutation response.

kotlin
// GraphQL mutation
mutation CreatePost($input: CreatePostInput!) {
    createPost(input: $input) {
        id
        title
        createdAt
        author {
            id
            name
        }
    }
}

// Mutation call with input type
val input = CreatePostInput(
    title = "New post about GraphQL",
    content = "GraphQL simplifies working with API..."
)
val result = apolloClient
    .mutation(CreatePostMutation(input))
    .execute()

An important advantage of GraphQL over REST in the context of mobile development is automatic code generation. Apollo Client for Kotlin (Apollo GraphQL) generates type-safe classes from .graphql files at build time. If the server changes the schema, the project will not build until the queries are updated. This prevents runtime errors typical of REST, where changes to the response structure may go unnoticed during development.

Ecosystem: Apollo, Relay, and Tools

The GraphQL ecosystem includes several key libraries and tools that simplify development and operation. Apollo Client is the most popular client library, supporting React, iOS, Android, and Kotlin Multiplatform. Relay by Facebook is an alternative for React applications with a unique approach to data management and caching. The choice between Apollo and Relay depends on the platform and performance requirements.

On the server side, the leaders are Apollo Server (Node.js), Netflix DGS Framework (Kotlin/Java), and graphql-ruby. For schema development and query testing, GraphiQL is used — an interactive IDE built into the browser. Apollo Studio provides performance metrics, query tracing, and schema management for production environments. Separately worth mentioning is GraphQL Code Generator — a tool that generates TypeScript, Kotlin, Swift, and Dart types from an SDL schema.

For mobile development, Apollo Kotlin (Apollo GraphQL) is of particular interest — a library entirely written in Kotlin with support for coroutines, Flow, and Multiplatform. It allows using unified GraphQL queries for Android and iOS in Kotlin Multiplatform projects. Apollo Kotlin normalizes the cache, supports field-level errors (partial errors), and automatically generates data models from .graphql files. This makes GraphQL the preferred choice for large mobile projects where development speed and type safety are important.

Frequently Asked Questions

Does GraphQL replace REST?

GraphQL does not replace REST, but offers an alternative approach. REST is better suited for simple CRUD APIs, HTTP caching, and public APIs with predictable load. GraphQL is optimal for complex interfaces with many related data points.

Is it difficult to migrate from REST to GraphQL?

Migration is possible gradually: GraphQL can work as a layer (gateway) in front of existing REST services. Many companies add GraphQL alongside REST without turning off the old API. A full replacement requires rewriting resolvers.

What is the N+1 problem in GraphQL?

N+1 occurs when a separate database query is executed for each item in a list. It is solved using DataLoader — a library that batches individual requests into one and caches results within a single HTTP request.

How does GraphQL handle file uploads?

The GraphQL specification does not directly define file uploads. In practice, the following are used: base64 encoding (simple but inefficient for large files), multipart requests following the graphql-multipart-request-spec protocol, or a separate REST endpoint for files.

Is GraphQL secure?

GraphQL security requires additional measures: limiting nesting depth, query complexity limits, rate limiting at the operation level. Public schema introspection can reveal the data structure — it is recommended to disable it in production.

Summary

  • GraphQL — a query language where the client controls the response structure, eliminating overfetching and underfetching
  • Three types of operations: query (reading), mutation (writing), subscription (real-time)
  • Uses a single endpoint and a strict type system — SDL schema
  • Unlike REST, solves the multiple round-trips problem — all data in one request
  • Requires DataLoader to prevent the N+1 problem and manual caching configuration
  • Main clients: Apollo Client (Android, iOS, Web) and Relay (React)
  • Best suited for complex interfaces with many related entities and mobile applications

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also