What is GraphQL?
We will be starting to learn about GraphQL and using it in an iOS App
In this next few articles we will be starting to learn about GraphQL and using it in an iOS App. I will assume you know nothing about GraphQL about
Imagine you are ordering food at a restaurant. With a traditional approach, you get a fixed meal — maybe a burger, fries, and a drink — whether you wanted the fries or not. You have no control over what comes on the plate. That is how REST APIs work: you call an endpoint, and the server decides exactly what data to send back.
Now imagine a restaurant where you write your own order from scratch. You say: "I want the burger, hold the fries, add extra pickles, and give me a milkshake instead of water." The kitchen gives you exactly what you asked for — nothing more, nothing less. That is GraphQL.
GraphQL (Graph Query Language) is a query language for APIs invented by Facebook in 2012 and open-sourced in 2015. Instead of the server deciding what shape the data takes, the client decides. You write a query describing exactly the data you need, send it to a single endpoint, and receive a JSON response that mirrors the shape of your query.
GitHub was one of the earliest major adopters of GraphQL. They built their entire v4 API using GraphQL because their REST API (v3) was struggling with the complexity of the data relationships between users, repositories, issues, pull requests, and more. With REST, fetching a repository's details alongside its issues and contributors required multiple round-trips. With GraphQL, it is a single query.
The Three Pillars of GraphQL
Queries — Reading Data
A query is how you ask a GraphQL server for data. Think of it as a GET request, but far more flexible. Here is a simple query that asks GitHub for your username and bio:
query {
viewer {
login
bio
avatarUrl
}
}
And the server responds with:
{
"data": {
"viewer": {
"login": "octocat",
"bio": "I love coding!",
"avatarUrl": "https://avatars.githubusercontent.com/u/123456"
}
}
}
Notice how the response shape mirrors the query shape exactly. This is one of GraphQL's most powerful features — you always know what you are going to get back.
Mutations — Writing Data
Mutations are how you create, update, or delete data on the server. They are like POST, PUT, or DELETE in REST. For example, starring a repository on GitHub:
mutation {
addStar(input: { starrableId: "MDEwOlJlcG9zaXRvcnkxMjM0NTY=" }) {
starrable {
stargazerCount
}
}
}
Every mutation requires an input object (the data you are sending) and returns a payload (the data you want back after the change is made).
Subscriptions — Real-Time Data
Subscriptions let you listen for real-time updates over a WebSocket connection. When data changes on the server, your client gets notified automatically. GitHub's public GraphQL API does not currently support subscriptions, but we will discuss the concept and how Apollo iOS handles them.
Key Vocabulary
| Term | What It Means | Analogy |
|---|---|---|
| Schema | The contract defining all available types and fields | A restaurant's complete menu |
| Query | A request for specific data | Your food order |
| Mutation | A request to change data | Asking the chef to modify a dish |
| Subscription | A real-time listener for data changes | A waiter bringing you updates |
| Resolver | Server function that fetches data for a field | The chef cooking your order |
| Type | A shape of data (like a Swift struct) | A category on the menu |
| Field | A single piece of data on a type | An item within a category |
| Argument | A parameter passed to a field | "Extra pickles, no onion" |
| Fragment | A reusable set of fields | A combo meal you can reference anywhere |
| Node | An object with a global ID | A specific dish with a ticket number |
| Edge | A relationship between two nodes | The link between "dish" and "ingredient" |
| Connection | A paginated list of edges | A multi-page section of the menu |
How a GraphQL Request Works Under the Hood
Every GraphQL request, regardless of whether it is a query or mutation, follows the same HTTP pattern:
- HTTP Method: Always
POST(the operation type is defined inside the body, not by the HTTP verb) - Endpoint: Always the same URL — for GitHub, it is
https://api.github.com/graphql - Headers: Must include
Authorization: Bearer YOUR_TOKENandContent-Type: application/json - Body: A JSON object with a
querystring (your GraphQL operation), and optionallyvariablesandoperationName
POST https://api.github.com/graphql
Authorization: Bearer ghp_xxxxxxxxxxxx
Content-Type: application/json
{
"query": "query { viewer { login } }",
"variables": {},
"operationName": null
}
GraphQL vs REST — A Detailed Comparison
If you have ever built an iOS app, you have almost certainly used REST APIs. Understanding how GraphQL differs from REST will help you appreciate why companies like GitHub, Shopify, and Yelp have adopted it.
Endpoints
REST: Each resource has its own URL. To get a GitHub user, you call GET /users/octocat. To get their repos, you call GET /users/octocat/repos. To get a specific repo's issues, you call GET /repos/octocat/hello-world/issues. Three different endpoints, three separate network calls.
GraphQL: There is one single endpoint: https://api.github.com/graphql. Every request goes to the same address. The query you write in the request body determines what data you get back. You could fetch the user, their repos, and the issues on those repos in a single request.
Over-Fetching and Under-Fetching
Over-fetching means getting more data than you need. GitHub's REST endpoint for a repository returns over 100 fields — owner info, license details, permissions, URLs for every resource — even if you only need the name and star count.
Under-fetching means not getting enough data in one call. To show a GitHub profile page with the user's info, their pinned repos, and recent contributions, you might need 4–5 separate REST calls. Each call adds latency, especially on mobile networks.
GraphQL eliminates both problems. You specify exactly which fields you need, and you get everything in one round-trip.
A Concrete Example with GitHub
Imagine building a screen that shows a GitHub user's name, avatar, and their 5 most recent repositories with star counts.
With REST (3 requests):
GET /users/octocat → 100+ fields, you need 3
GET /users/octocat/repos?per_page=5 → 80+ fields per repo, you need 2
GET /users/octocat/repos?sort=updated → Might not give you what you need
With GraphQL (1 request):
query {
user(login: "octocat") {
name
avatarUrl
repositories(last: 5, orderBy: { field: UPDATED_AT, direction: DESC }) {
nodes {
name
stargazerCount
}
}
}
}
One request. Exactly the fields you need. No wasted bandwidth.
Side-by-Side Comparison
| Feature | REST | GraphQL |
|---|---|---|
| Endpoints | Many (one per resource) | One (single endpoint) |
| Data shape | Decided by the server | Decided by the client |
| Over-fetching | Common problem | Eliminated by design |
| Under-fetching | Requires multiple calls | Single query gets all data |
| Versioning | Often /v1/, /v2/, /v3/ |
Schema evolves, no versioning needed |
| Documentation | Swagger/OpenAPI (separate) | Schema IS the documentation (introspection) |
| Type safety | Requires separate type definitions | Built into the schema |
| Error handling | HTTP status codes (404, 500) | Always returns 200; errors in response body |
| Caching | HTTP caching (ETags, Cache-Control) | Requires a smart client (Apollo) |
| File uploads | Straightforward (multipart) | Requires special handling |
| Learning curve | Low (very familiar) | Moderate (new syntax and concepts) |