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
GraphQL
By Bartosz Sypytkowski
GraphQL
- 1,220