Share
Embed
Transcript
104 slides
-
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 -
History of GraphQL July ’15 Aug ’12 Evolution Open Source
Feb ’12 May ’17 Prototype -
Jan ’12 Prototype iOS Client {stories: {…}} SELECT * FROM
stories Server -
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 -
Jan ’12 Prototype iOS Client {stories: {…}} SELECT * FROM
stories Server -
Jan ’12 Prototype iOS Client FQL Business Logic + Storage
Layer {stories: {…}} SELECT * FROM stories -
Feb ’12 Prototype iOS Client Business Logic + Storage Layer
{stories: {…}} stories.fields(title) -
Feb ’12 Prototype iOS Client GQL Business Logic + Storage
Layer {stories: {…}} stories.fields(title) -
<?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 -
query { me { name profilePicture { height width uri
} } } Codegen -
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 -
query { me { name ...ProfilePicture } } fragment ProfilePicture
on User { profilePicture { height width uri } } -
<?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 -
<?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 -
GraphQL Clients circa 2013 Dispatcher Action Store View Action GraphQL
Queries GraphQL Engine -
GraphQL Clients circa 2013 Dispatcher Action Store View Action GraphQL
Queries GraphQL Engine Immutable Model Store -
GraphQL Clients circa 2013 Dispatcher Action Store View Action GraphQL
Queries GraphQL Engine Immutable Model Store Reactive Views -
GraphQL Clients circa 2013 GraphQL Engine GraphQL Queries Immutable Model
Store Reactive Views GraphQL Mutations -
GraphQL Clients circa 2013 GraphQL Engine GraphQL Queries Immutable Model
Store Reactive Views GraphQL Mutations -
REST Response REST POST Query Response GraphQL Query Batch API
Client Shim GraphQL Mutation Mutation Response -
{ "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 -
mutation PageLikeMutation( $page_id: String!, $analytics: [String!] ) { page_like(page_id: $page_id,
analytics: $analytics) { page { id likers { count } } } } -
+ (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 -
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 -
type User { name: String! profilePicture(size: Int = 50): ProfilePicture
friends(first: Int): [User!]! events(first: Int): [Event!]! } -
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>, |}; -
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!]! } -
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!]! } -
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!]! } -
query Timeline { me { displayName: name } } query
Bookmarks { me { displayName: nickname } } -
type User = {| displayName: ?string |}; query Timeline {
me { displayName: name } } query Bookmarks { me { displayName: nickname } } -
query ProfilePic { me { pic: profilePicUri } } query
CoverPhoto { me { pic: coverPhoto { uri } } } -
type User = {| pic: any |}; query ProfilePic {
me { pic: profilePicUri } } query CoverPhoto { me { pic: coverPhoto { uri } } } -
type UserWithPic = {| profilePicture: ?ProfilePicture |}; type ProfilePicture =
{| uri: string |}; fragment UserWithPic on User { profilePicture { uri } } -
query { me { name nickname # Only if in
nickname test group } }