From Rest to GraphQL

Agenda

  • Story of REST
  • Problems of REST
  • GraphQL: a solution?
  • Problems of GraphQL...
  • ...and how to avoid them

REST in a nutshell

GET http://api.com/products/281

Read

POST http://api.com/products/

Create

PUT http://api.com/products/281

Update/replace

DELETE http://api.com/products/281

Delete

REST-ful of problems

  • Not a standard
  • HTTP request/response
  • Schemaless
  • Versioning
  • Under- and over-fetching

What does it mean to be RESTful?

Under- and over-fetching

Domain

Under- and over-fetching

What service offers

Under- and over-fetching

What client expects

Under- and over-fetching

A doubtful solution

Order GetOrderById(int orderId);
Order GetOrderByName(string name);
Order GetOrderWithProductDetails(int orderId);
Order GetOrderWithCustomerInfo(int orderId);
Order GetOrderWithCustomerInfoByOrderName(string name);
Order GetOrderWithShippingData(int orderId);
Order GetOrderWithShippingDataAndCustomerInfo(int orderId);

Time for GraphQL

How does it look like?

{
    "id": 1337,
    "title": "Horse steroids - guide for athletes",
    "author": {
        "firstName": "Andy",
        "lastName": "Greenberg"
    },
    "created": "2015-05-22T14:56:29.000Z",
    "comments": [
        {
            "author": {
                "firstName": "Sam",
                "lastName": "Gonagal"
            },
            "created": "2015-05-22T14:56:34.000Z"
        },
        {
            "author": {
                "firstName": "Christ",
                "lastName": "McSwagger"
            },
            "created": "2016-05-22T14:56:34.000Z"
        }
    ]
}
query {
    article(id: 1337) {
        id
        title
        author {
            ...fullName
        }
        created
        comments {
            author {
                ...fullName
            }
            created
        }
    }
}
fragment fullName on User {
    firstName
    lastName
}

Request

Response

How does it feel like?

GraphQL

CRUD? Not anymore.

Query

Mutation

query RepositoryWatchers {
  repository(owner:"fsprojects", name:"Paket") {
    id
    name
    watchers(first:20) {
      edges {
        node {
          ... on User {
            login
          }
	}
      }
    }
  }
}
mutation Comment($data:AddCommentInput!) {
  addComment(input:$data) {
    clientMutationId
    commentEdge {
      node {
        ... on IssueComment {
          createdAt
        }
      }
    }
  }
}

Read only

Read-write

Schema

Everything is statically typed

type Query {
  animal(name: String!): Animal @deprecated(reason: "lol")
  cat(name: String!): Cat
  dog(name: String!): Dog
}

interface Animal {
  id: String!
  name: String
}

type Dog implements Animal {
  id: String!
  name: String
  role: Role
}

type Cat implements Animal {
  id: String!
  name: String
}

enum Role { Watchdog, Hunter, Pet }

Introspection API

query { 
  __schema {
    types {
      name
      fields {
        name
        type {
          name
        }
      }
    }
  }
}

Use GraphQL query to get metadata about GraphQL itself

DEMO

Think about the Domain as a whole instead of single actions

Capability-oriented design

What service offers

Capability-oriented design

What user expects to get

1st request

Capability-oriented design

Extra: what user expects to get (incrementally)

2nd request

GraphQL on the client side

Relay

Apollo

  • Opinionated
  • Requires schema constraints
  • Cursor-based pagination
  • Only for React / React Native
  • Declarative but verbose mutation definitions and cache invalidation
  • Adaptable
  • Requires some configuration
  • Cursor- or page-based pagination
  • React/Redux, Angular2, Meteor
  • Integrated with Redux store pattern

Apollo

Example component

const FeedWithData = graphql(gql`
  {
    feed (type: TOP, limit: 5) {
      repository {
        owner { login }
        name
      }
  
      postedBy { login }
    }
  }
`)(Feed);

function Feed({ data }) {
  return (
    <View>
      <Text style={styles.title}>GitHunt</Text>
      { data.feed.map((item) => 
          <View style={styles.feedItem}>
            <Text style={styles.entry}>
              {item.repository.owner.login}/{item.repository.name}
            </Text>
            <Text>Posted by {item.postedBy.login}</Text>
          </View>) }
    </View>
  );
}

Downsides

Development speed over machine speed

Generic behavior over specialized logic

N+1 SELECTs

If every query is dynamic, how to predict required 3rd party resources?

var orders = db.Customers.SelectMany(u => u.Orders);

foreach (var order in orders) {
    foreach (var product in order.Products) {
        DoSomething(product);
    }
}

Great with relational data but shitty with aggregations

Summary

End

Bartosz Sypytkowski

@horusiath

b.sypytkowski@gmail.com

http://bartoszsypytkowski.com

Made with Slides.com