Upgrade to Pro — share decks privately, control downloads, hide ads and more …

GraphQL: Client-Driven Development

GraphQL: Client-Driven Development

GraphQL Europe 2017

Dan Schafer

May 21, 2017
Tweet

More Decks by Dan Schafer

Other Decks in Programming

Transcript

  1. Product-centric: GraphQL is unapologetically driven by the requirements of views

    and the front-end engineers that write them. We start with their way of thinking and requirements and build the language and runtime necessary to enable that. https://facebook.github.io/react/blog/2015/05/01/graphql-introduction.html
  2. Jan ’12 Prototype iOS Client {stories: {…}} SELECT * FROM

    stories Server SELECT stories.id, stories.title, users.name, COUNT(likers.id) FROM stories LEFT JOIN users ON users.id = stories.author_id LEFT JOIN likers ON likers.post_id = stories.id LIMIT 10
  3. Jan ’12 Prototype iOS Client FQL Business Logic
 +
 Storage

    Layer {stories: {…}} SELECT * FROM stories
  4. Feb ’12 Prototype iOS Client GQL Business Logic
 +
 Storage

    Layer {stories: {…}} stories.fields(title)
  5. <?xml version='1.0' encoding='utf-8'?> <model documentVersion="1.0"> <entity name="User" representedClassName="FBUser" syncable="YES"> <attribute

    attributeType="String" name="name" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Lorem" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Ipsum" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Dolor" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Sit" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Amet" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Lorem" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Ipsum" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Dolor" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Sit" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Amet" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Lorem" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Ipsum" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Dolor" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Sit" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Amet" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Lorem" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Ipsum" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Dolor" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Sit" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Amet" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Lorem" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Ipsum" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Dolor" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Sit" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Amet" optional="YES" syncable="YES" /> <userInfo> <entry key="fb.graphQLType" value="User" /> </userInfo> </entity> <elements> <element height="255" name="User" positionX="160" positionY="192" width="128" /> </elements> </model> CoreData XML Files
  6. query { me { name profilePicture { height width uri

    } } } <?xml version='1.0' encoding='utf-8'?> <model documentVersion="1.0"> <entity name="User" representedClassName="FBUser" syncable="YES"> <attribute attributeType="Boolean" name="isOwned" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Lorem" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Ipsum" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Dolor" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Sit" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Amet" optional="YES" syncable="YES" /> <userInfo> <entry key="fb.graphQLType" value="User" /> </userInfo> </entity> <elements> <element height="255" name="User" positionX="160" positionY="192" width="128" /> </elements> </model> Codegen
  7. query { me { name ...ProfilePicture } } fragment ProfilePicture

    on User { profilePicture { height width uri } }
  8. <?xml version='1.0' encoding='utf-8'?> <model documentVersion="1.0"> <entity name="User" representedClassName="FBUser" syncable="YES"> <attribute

    attributeType="String" name="name" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Lorem" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Ipsum" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Dolor" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Sit" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Amet" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Lorem" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Ipsum" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Dolor" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Sit" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Amet" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Lorem" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Ipsum" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Dolor" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Sit" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Amet" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Lorem" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Ipsum" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Dolor" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Sit" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Amet" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Lorem" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Ipsum" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Dolor" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Sit" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Amet" optional="YES" syncable="YES" /> <userInfo> <entry key="fb.graphQLType" value="User" /> </userInfo> </entity> <elements> <element height="255" name="User" positionX="160" positionY="192" width="128" /> </elements> </model> CoreData XML Files
  9. <?xml version='1.0' encoding='utf-8'?> <model documentVersion="1.0"> <entity name="User" representedClassName="FBUser" syncable="YES"> <attribute

    attributeType="String" name="name" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Lorem" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Ipsum" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Dolor" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Sit" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Amet" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Lorem" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Ipsum" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Dolor" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Sit" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Amet" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Lorem" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Ipsum" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Dolor" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Sit" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Amet" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Lorem" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Ipsum" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Dolor" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Sit" optional="YES" syncable="YES" /> <attribute attributeType="String" name="Amet" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Lorem" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Ipsum" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Dolor" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Sit" optional="YES" syncable="YES" /> <attribute attributeType="Boolean" name="Amet" optional="YES" syncable="YES" /> <userInfo> <entry key="fb.graphQLType" value="User" /> </userInfo> </entity> <elements> <element height="255" name="User" positionX="160" positionY="192" width="128" /> </elements> </model> CoreData XML Files
  10. GraphQL Clients circa 2013 Dispatcher Action Store View Action GraphQL


    Queries GraphQL
 Engine Immutable
 Model
 Store
  11. GraphQL Clients circa 2013 Dispatcher Action Store View Action GraphQL


    Queries GraphQL
 Engine Immutable
 Model
 Store Reactive
 Views
  12. REST Response REST POST Query Response GraphQL Query Batch API

    Client Shim GraphQL Mutation Mutation Response
  13. { "page_like": { "graph_endpoint": { "method": "POST", "url": { "format":

    "%@/likes", "variables": ["${page_id}"] }, "params": { "analytics": "JSON(${analytics})" } }, "input": { "page_id": "String!", "analytics": "[String!]" }, "payload": { "page": { "type": "Page", "query": "{node($id){...Contents}}", "query_variables": { "id": "${page_id}" } } } } } mutations.json
  14. + (FBNetworkerRequest *)requestWithInput:(FBPageLikeInputData *)input withGraphQLQuery:(FBGraphQLRequest *)query { NSString *path =

    [NSString stringWithFormat:@"%@/likes", input.pageId]; FBGraphRequest *mutationRequest = [[FBGraphRequest alloc] initWithPath:path]; FBGraphBatchRequest *batchRequest = [[FBGraphBatchRequest alloc] initWithRequest:mutationRequest]; FBGraphQLRequest *queryRequest = [[FBGraphQLRequest alloc] initWithQuery:query]; [batchRequest addGraphRequest:queryRequest]; return batchRequest; } Auto-Generated Code Auto-Generated Code
  15. One of the big ideas was query colocation — the

    notion that you should be able to specify your data requirements for each view component inside the view itself and that the framework should transparently handle aggregation and efficient fetching. https://wincent.com/blog/relay-modern
  16. type User { name: String! profilePicture(size: Int = 50): ProfilePicture

    friends(first: Int): [User!]! events(first: Int): [Event!]! }
  17. type User { name: String! profilePicture(size: Int = 50): ProfilePicture

    friends(first: Int): [User!]! events(first: Int): [Event!]! } type User = {| name: string, profilePicture: ?ProfilePicture, friends: Array<User>, events: Array<Event>, |};
  18. type User = {| name: string, profilePicture: ?ProfilePicture, friends: Array<User>,

    events: Array<Event>, |}; type User { name: String! profilePicture(size: Int = 50): ProfilePicture friends(first: Int): [User!]! events(first: Int): [Event!]! }
  19. type User = {| name: string, profilePicture: ?ProfilePicture, friends: Array<User>,

    events: Array<Event>, |}; type User { name: String! profilePicture(size: Int = 50): ProfilePicture friends(first: Int): [User!]! events(first: Int): [Event!]! }
  20. type User = {| name: ?string, profilePicture: ?ProfilePicture, friends: ?Array<?User>,

    events: ?Array<?Event>, |}; type User { name: String! profilePicture(size: Int = 50): ProfilePicture friends(first: Int): [User!]! events(first: Int): [Event!]! }
  21. query Timeline { me { displayName: name } } query

    Bookmarks { me { displayName: nickname } }
  22. type User = {| displayName: ?string |}; query Timeline {

    me { displayName: name } } query Bookmarks { me { displayName: nickname } }
  23. query ProfilePic { me { pic: profilePicUri } } query

    CoverPhoto { me { pic: coverPhoto { uri } } }
  24. type User = {| pic: any |}; query ProfilePic {

    me { pic: profilePicUri } } query CoverPhoto { me { pic: coverPhoto { uri } } }
  25. type UserWithPic = {| profilePicture: ?ProfilePicture |}; type ProfilePicture =

    {| uri: string |}; fragment UserWithPic on User { profilePicture { uri } }
  26. query { me { name nickname # Only if in

    nickname test group } }