Introduction to GraphQL

@attheodo

What is GraphQL

  • GraphQL is a query language for APIs.
  • GraphQL is a server-side runtime for executing queries by using a type system you define for your data.

Where we at now?

  • Resources:

REST APIs

http://api.mysite.com/v2/meetups
http://api.mysite.com/v2/meetups/12
http://api.mysite.com/v2/meetups/attendees
http://api.mysite.com/v2/meetups/attendees/13
  • Actions:
    • ​GET, POST, PUT, DELETE
  • Augmentation of HTTP status code for application-level errors:
    • 200 (OK), 400 (Bad request )...
  • HATEOAS

What's wrong with REST?

  • The server determines what data it will send to the client:
GET http://api.mysite.com/v2/meetups/12

{
    "meetup": {
        "id": 12
        "name": "Dead Man's Chest"
        "date": "20170221T123446Z"
        "attendeesIds": [
            123,
            12
            223
            21
        ]
    }
}
  • What if we want to display the attendees names in the same view:
    • ​4 extra HTTP calls!

What's wrong with REST?

  • After popular demand...
GET http://api.mysite.com/v3/meetups/12

{
    "meetup": {
        "id": 12
        "name": "Dead Man's Chest"
        "date": "20170221T123446Z"
        "attendees": [
            {
               "id": 123
               "name": "Vassilis"
            },
            {
               "id": 12
               "name": "George"
            }
        ]
    }
}
  • Becomes a PITA to manage legacy clients still using v2 API...

What's wrong with REST?

  • You might need a combination of HTTP calls to the API to get the data you need:
    • ​Latency
    • Complex error handling
  • As the API evolves it's difficult to manage and maintain client & server code compatibility.

In short...

What does GraphQL bring to the table?

  • Ask for what you need and get exactly that:
    • The clients control the data they get, not the server.
  • Get many resources in a single request:
    • ​Follow references between resources
    • Less HTTP traffic
  • Type system:
    • GraphQL uses types to ensure clients only ask for what's possible
    • Schema introspection - autogenerated code
  • APIs evolve without versions:
    • Type system allows new fields and types to be added.
    • Deprecation semantics

GraphQL in a nutshell

  • One endpoint:
    • http://api.mysite.com/graphql
  • Queries
    • Fetch resources from the server (GET)
  • Mutations
    • Modify a resource in the server (POST)
  • Response
{
  "data": {
      ... what you asked for ...
  }
  "errors": [
    {
      "message": "blah"
    }
  ]
}

GraphQL (Queries)

// Request
{
  attendee {
    id
    name
  }
}
// Response
{
  "data": {
    "attendee": {
       "id": 12
       "name": "Vassilis"
     }
  }
}
// Request
{
  attendee {
    id
    name
    birthdate
  }
}
// Response
{
  "data": {
    "attendee": {
       "id": 12
       "name": "Vassilis"
       "birthdate": "20170221T123446Z"
     }
  }
}

GraphQL (Queries with Arguments)

// Request
{
  member(id: "12") {
    id
    name
    birthday(type: FORMATTED)
  }
}
// Response
{
  "data": {
    "member": {
       "id": 12
       "name": "Vassilis"
       "birthdate": "13 Jan 1986"
     }
  }
}

enum argument! Much Wow!

GraphQL (Queries & Aliases)

// Request
{
  attendee: member(id: "12") {
    id
    fullname: name
    birthday(type: FORMATTED)
  }
}
// Response
{
  "data": {
    "attendee": {
       "id": 12
       "fullname": "Vassilis"
       "birthdate": "13 Jan 1986"
     }
  }
}
  • If you have naming OCD...
  • ... you can basically alias everything to your preferences

GraphQL (Queries & Fragments)

// Request
{
 meetup {
   id
   name
   location
   start_date
   end_date
   rsvp_url
   cover_photo {
      url
      size
   }
 }
}
// Request
{
 meetup {
   ...MeetupDetails
 }
}

fragment MeetupDetails on Meetup {
  id
  name
  location
  start_date
  end_date
  rsvp_url
  cover_photo {
    url
    size
  }
}
  • Allows you to re-use defined subqueries in multiple places

GraphQL (Queries with variables)

// Request

query GetMeetupByStartDate($start_date: Date) {
 meetup(start_date: $start_date) {
   id
   name
   location
 }
}

{
  "start_date": "2017-02-21T16:42:34+00:00"
}
// Response

{
  "data": {
     "id": 1,
     "name": "Dead Man's chest",
     "location": "Coho"    
   }
}

Operation Name

Useful for debugging stuff on the server side

GraphQL (Mutations)

// Request

query CreateMeetup($name: String!, location: String!, $start_date: Date) {
 createMeetup(name: $name, location: $location, start_date: $start_date){
   id
 }
}

{
  "name": "The Return of the Mummy",
  "location": "OK!Thess"
}

// Response
{
  "data": {
     "id": 100
   }
}

If the type name has no "!", it's optional

$start_date can be omitted from the mutation

GraphiQL

  • Web client for your GraphQL API.
    • Let's you create and debug your queries and mutations with ease.

DEMO

Consuming GraphQL APIs from iOS

  • The traditional way:
    • ​Create static methods that return you query/mutation strings.
    • Define native objects to get your results de-serialized from the API responses.

Consuming GraphQL APIs from iOS

The traditional way

struct GraphQL {
    static func getAttendee(id: String) {
        return "query {" +
               "  attendee(id: \"\(id)\") {" +
               "     id"   +
               "     name" +
               "     birthdate" +
               "   }"+
               "}"
             
    }
}

// ... feed it in an Alamofire GET request

final class Attendee: NSObject, ResponseObjectSerializable {

    let id: String?
    let name: String?
    let birthdate: Date?

    init?(representation: AnyObject) {
        id = representation.value(forKeyPath: "id") as? String
        name = representation.value(forKeyPath: "name") as? String
        birthdate = representation.value(forKeyPath: "birthdate") as? Date
    }

}

Consuming GraphQL APIs from iOS

The traditional way

Consuming GraphQL APIs from iOS

The traditional way

  • You miss all the typed goodness
    • ​Need to replicate server types into native objects
    • You don't know whether you query/mutations are valid
  • Creating complex queries is huge PITA
    • Creating them on GraphiQL and then pasting them in a one-liner string is the sanest way to do it.
  • Fragments?

Consuming GraphQL APIs from iOS

The shiny new toy

A strongly-typed, caching GraphQL client for iOS, written in Swift.

Consuming GraphQL APIs from iOS

APOLLO

1. Download your schema

apollo-codegen download-schema http://localhost:8080/graphql --output schema.json

2. Add a build phase to your project

$APOLLO_FRAMEWORK_PATH/check-and-run-apollo-codegen.sh \
    generate $(find . -name '*.graphql') \
    --schema schema.json \
    --output API.swift

3. Define your queries & mutations in .graphql files

4. Build and enjoy

Consuming GraphQL APIs from iOS

APOLLO

// In Queries.graphql
query GetAttendee {
  attendee {
    id
    name
    birthdate
  }
}

// In your API class
apollo.fetch(query: GetAttendee()) { (result, error) in
  print(result?.data?.attendee?.name) // Vassilis
}

Is GraphQL ready from Prime Time?

Questions?

@attheodo

http://attheo.do

Introduction to GraphQL

By Thanos Theodoridis

Introduction to GraphQL

  • 425