Skip to content
advanced Phase 5 · Networking & Data

GraphQL

Integrate GraphQL APIs using Apollo iOS or native URLSession with queries and mutations.

55m
3 problems
Topic Progress 0%

GraphQL Basics

What is GraphQL?

GraphQL is a query language for APIs that lets clients request exactly the data they need. Unlike REST, where the server defines response shapes, GraphQL clients specify the fields they want.

Schema Definition

GraphQL uses a strongly-typed schema:

type User {
  id: ID!
  name: String!
  email: String!
  posts: [Post!]!
}

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

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

type Mutation {
  createUser(name: String!, email: String!): User!
  updateUser(id: ID!, name: String): User!
  deleteUser(id: ID!): Boolean!
}

Queries

Clients request specific fields:

query GetUsers {
  users {
    id
    name
    email
  }
}

This returns only the requested fields with no over-fetching.

Nested Queries and Variables

GraphQL handles relationships naturally:

query GetUserWithPosts($id: ID!) {
  user(id: $id) {
    name
    posts {
      title
      body
    }
  }
}

Variables are passed separately from the query string as a dictionary.

Mutations

Modify data with mutations:

mutation CreateUser($name: String!, $email: String!) {
  createUser(name: $name, email: $email) {
    id
    name
  }
}

Fragments

Reuse field selections with fragments:

fragment UserFields on User {
  id
  name
  email
}

query GetUsers {
  users {
    ...UserFields
  }
}

GraphQL vs REST

REST has fixed endpoints returning full objects. GraphQL has one endpoint where clients specify exact data needs. This eliminates over-fetching and under-fetching but adds complexity in schema design and caching.

Apollo iOS

Setting Up Apollo iOS

Apollo iOS generates type-safe Swift code from your GraphQL schema.

Installation

Add Apollo to your SPM dependencies:

dependencies: [
    .package(url: "https://github.com/apollographql/apollo-ios.git", from: "1.0.0")
]

Schema Download

Download your schema:

apollo-cli download-schema https://your-api.com/graphql --output schema.graphqls

Code Generation

Create an Apollo code generation config:

# apollo-codegen-config.yml
schema: schema.graphqls
sources:
  - schema: schema.graphqls
    sources:
      - graphql
output:
  module:
    type: swiftPackageManager
    target: GraphQLAPI
  path: ./Sources/GraphQLAPI

Run code generation:

apollo-cli generate apollo-codegen-config.yml

Creating a Client

import Apollo
import ApolloHTTP

let httpTransport = HTTPTransport(url: URL(string: "https://your-api.com/graphql")!)
let client = ApolloClient(transport: httpTransport)

Fetching Data

Use the generated query types:

let query = GetUsersQuery()

client.fetch(query: query) { result in
    switch result {
    case .success(let graphQLResult):
        if let users = graphQLResult.data?.users {
            // Use typed user data
        }
    case .failure(let error):
        print("Error: \(error)")
    }
}

// Async/await
let result = try await client.fetch(query: query)

With Authorization

Add headers for authenticated requests:

let transport = HTTPTransport(url: url)
transport.additionalHeaders = [
    "Authorization": "Bearer \(token)"
]

Queries and Mutations

Writing Queries

Define queries in .graphql files and let Apollo generate types:

query GetUserProfile($id: ID!) {
  user(id: $id) {
    id
    name
    email
    avatarURL
    posts {
      edges {
        node {
          id
          title
          createdAt
        }
      }
      pageInfo {
        hasNextPage
        endCursor
      }
    }
  }
}

Writing Mutations

mutation CreatePost($title: String!, $body: String!) {
  createPost(title: $title, body: $body) {
    id
    title
    body
    createdAt
  }
}

Handling Results

Access generated types with full type safety:

let query = GetUserProfileQuery(id: "42")

client.fetch(query: query) { result in
    guard let data = try? result.get().data else { return }
    
    let name = data.user?.name
    let posts = data.user?.posts?.edges?.compactMap { $0?.node } ?? []
}

Pagination

Implement cursor-based pagination:

class UserListViewModel: ObservableObject {
    @Published var users: [GetUsersQuery.Data.Users.Edge.Node] = []
    private var cursor: String?
    private var hasNextPage = true
    
    func loadMore() async {
        guard hasNextPage else { return }
        
        let query = GetUsersQuery(first: 20, after: cursor)
        let result = try? await client.fetch(query: query)
        
        if let data = result?.data {
            let newEdges = data.users?.edges?.compactMap { $0?.node } ?? []
            users.append(contentsOf: newEdges)
            cursor = data.users?.pageInfo?.endCursor
            hasNextPage = data.users?.pageInfo?.hasNextPage ?? false
        }
    }
}

Cache Policies

Apollo provides built-in caching:

let client = ApolloClient(
    cache: InMemoryCache(),
    transport: HTTPTransport(url: url)
)

// Return cache data, fetch if needed
client.fetch(query: query, cachePolicy: .returnCacheDataElseLoad)

// Always fetch from network
client.fetch(query: query, cachePolicy: .fetchIgnoringCacheData)

Quiz

1. What is the main advantage of GraphQL over REST?

Question 1 options

2. What does Apollo iOS provide?

Question 2 options

3. What is a GraphQL fragment?

Question 3 options

4. What cache policy fetches only from cache?

Question 4 options

Flashcards

Question

What is GraphQL?

Answer

A query language for APIs where clients specify exactly which fields they need from the server.

Question

What is a GraphQL fragment?

Answer

A reusable set of fields on a specific type that can be included in queries using the spread operator.

Question

How does Apollo iOS generate types?

Answer

Apollo downloads the GraphQL schema and generates Swift types from .graphql query files.

Question

What is the difference between queries and mutations?

Answer

Queries read data. Mutations modify data on the server and can return the modified object.

Revision Notes

Key Takeaways

  • 1. GraphQL eliminates over-fetching by letting clients request exact fields
  • 2. Apollo iOS generates type-safe Swift code from GraphQL schemas
  • 3. Fragments enable reusable field selections across queries
  • 4. Cursor-based pagination uses pageInfo for efficient data loading
  • 5. Apollo cache policies control when to use cached vs fresh data

Interview Tips

  • Explain the N+1 problem in GraphQL and how DataLoader solves it
  • Discuss when to use GraphQL vs REST for a given use case
  • Know how Apollo caching works with cache policies
  • Describe fragment reuse and its benefits for code maintainability

Cheat Sheet

GraphQL: One endpoint, clients specify data shape.
Schema: Types, Query, Mutation definitions.
Fragments: Reusable field selections (...FragmentName).
Apollo iOS: Generates Swift types from schema + queries.
Pagination: Cursor-based with pageInfo (hasNextPage, endCursor).
Cache: InMemoryCache with policies like .returnCacheDataElseLoad.