Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
GraphQL and Relay
Search
Helielson
January 29, 2016
Programming
2
170
GraphQL and Relay
Concepts, Spec and how we are using it at jusbrasil.com.br
Helielson
January 29, 2016
Tweet
Share
More Decks by Helielson
See All by Helielson
Relay @ Jusbrasil
helielson
0
150
Component-based development with React
helielson
1
130
React - A JavaScript library for building user interfaces
helielson
9
1.8k
Other Decks in Programming
See All in Programming
Click-free releases & the making of a CLI app
oheyadam
2
110
macOS でできる リアルタイム動画像処理
biacco42
9
2.3k
CSC509 Lecture 11
javiergs
PRO
0
180
受け取る人から提供する人になるということ
little_rubyist
0
220
現場で役立つモデリング 超入門
masuda220
PRO
15
3.2k
Boost Performance and Developer Productivity with Jakarta EE 11
ivargrimstad
0
1.8k
Realtime API 入門
riofujimon
0
150
Jakarta EE meets AI
ivargrimstad
0
440
AWS IaCの注目アップデート 2024年10月版
konokenj
3
3.3k
TypeScript Graph でコードレビューの心理的障壁を乗り越える
ysk8hori
2
830
Duckdb-Wasmでローカルダッシュボードを作ってみた
nkforwork
0
110
AWS Lambdaから始まった Serverlessの「熱」とキャリアパス / It started with AWS Lambda Serverless “fever” and career path
seike460
PRO
1
240
Featured
See All Featured
Product Roadmaps are Hard
iamctodd
PRO
49
11k
Fashionably flexible responsive web design (full day workshop)
malarkey
405
65k
Why You Should Never Use an ORM
jnunemaker
PRO
54
9k
It's Worth the Effort
3n
183
27k
Building Applications with DynamoDB
mza
90
6.1k
Ruby is Unlike a Banana
tanoku
96
11k
Adopting Sorbet at Scale
ufuk
73
9.1k
The Invisible Side of Design
smashingmag
297
50k
Bootstrapping a Software Product
garrettdimon
PRO
305
110k
Navigating Team Friction
lara
183
14k
Imperfection Machines: The Place of Print at Facebook
scottboms
264
13k
How to Ace a Technical Interview
jacobian
276
23k
Transcript
GraphQL and Relay
@helielson @hyetho @rodrigopr @rodrigoropr
Topics - GraphQL What is it? Motivation Spec Implementations How
we use it What is coming
Definition GraphQL is a query language designed to build client
applications by providing an intuitive and flexible syntax for describing their data requirements and interactions.
Why invent something new? What about REST and Ad hoc
endpoints?
REST problems - Multiple round trips to render single views
- Receives unnecessary data - Payloads tend to grow over time for all clients
- Code duplication - API changes with client need -
Hard to scale with multiple clients - Hard to keep backwards compatibility Ad hoc endpoints (view oriented)
How we think about data? - Not tables, joins or
URIs - But: - Objects - Properties - Relationships - What, not How
GraphQL
- Product-centric - Hierarchical - Client-specified queries - Strongly-typed Principles:
Product-centric
Hierarchical
{ post(id: 65) { title, votes, author { name, avatar
} } } Hierarchical
{ post(id: 65) { title, votes, author { name, avatar
} } } Hierarchical { "post": { "title": "Post title", "votes": 100, "author" { "name": "Didi", "avatar": "http://x.jpg" } } }
Strong-Typing
Client-specific Queries
Not tied to: - Language - Framework - Database -
Protocol
Schema - Think as a Graph - Define structure of
nodes (properties) - How they are connected
Schema User { name: String! email: String! followers: [User] isFriendOf(otherEmail:
String!): Boolean }
Schema query { post(postId: Int!): Post! user(email: String!): User! me:
User }
Type System - Interface - Object - Enum - Union
- List - Scalars: - String, Int, Boolean
Query language { user(email: "
[email protected]
") { name followers { name
} } }
query { user(email: "
[email protected]
") { ...userInfo followers { ...userInfo }
} } fragment userInfo on User { name, email lastPosts { ...somePostFrament } }
fragment petInfo on Pet { petName, owner { name }
… on Dog { noiseVolume: barkVolume } … on Cat { noiseVolume: meowVolume } }
Variables query houseTrainedQuery($atOtherHomes: Boolean) { dog { isHousetrained(atOtherHomes: $atOtherHomes) }
}
Fragment directives fragment maybeFragment on Query @include(if: $condition) { me
{ name } }
Fragment directives query myQuery($someTest: Boolean) { experimentalField @skip(if: $someTest) }
Mutation mutation { submitComment(postId: 1, user: "...", content: "") {
createdComment { id, url } } }
Open to optimizations - Batch - Cache - "DataLoader is
the true" http://github.com/facebook/dataloader
Network-GraphQL (how we are using) - Unify object definition -
Unify loader definition - Helper for thrift-style relationships - Add an extra optimization layer - Auto extract selections from query
Network-GraphQL (how we are using) - Define the schema based
on jusbrasil-types - Rethinking data structure - english - relationships - building as we need - Based on network Thrift API
What's coming next? - Open source movement - Improve data
coverage - Provide authorization in schema (acl) - Mutation (done) - Object authorization - Field authorization
Questions?
Relay
Topics - Relay What is it? Motivation Spec Implementation How
we use it What is coming
Definition A javascript framework for building data-driven react applications
Dispatcher View Store Action
Dispatcher View Store Action Server
Let's make an App !! Will be easy !!!!
None
How to load data from server?
<TopicTag /> <Feed /> <Document />
A lot of trips between server and client /feed #
get feed (documents ids) /documents/{id} # get a document /topics/{id} # get a topic Reuse the endpoint
A big payload /feed_with_topics_max_tree_documents One custom endpoint that loads everything
/feed?topics=true&max_documents=3 Multiple custom endpoints
And what about: Pagination? Ordering? Error handling? Retry? Caching?
Don't forget about writes: Conflicts? Write Coordination? Latency Compensation? State
Sync?
Nah... We can solve this! Let's combine X + Y
+ Z and solve it!
None
Want to introduce changes ??? First learn about the entire
project … :(
- How to fetch data - How to coordinate requests
- How to coordinate writes - How to handle retries/errors - How to keep server/app in sync - How to update view with changes - How to cache data - How to keep that all in your head - How to …. - Your App What you had to think about:
- What data to fetch - Your App What you
want to think:
Focus on what matters most! Relay make easy for developers
to focus on the product and forget about data access nuances
Declarative Colocation Mutations Principles
Declarative The component describe what it want
// Story.js export default class Story extends React.Component { render()
{ var story = this.props.story; return ( <View> <Image uri={story.author.profilePicture.uri} /> <Text>{story.author.name}</Text> <Text>{story.text}</Text> </View> ); } }
fragment on Story { text, author { name, profilePicture {
uri } } }
export class Story extends Component { … } export default
Relay.createContainer(Story, { fragments: { story: () => Relay.QL` fragment on Story { author { name profilePicture { uri } } text } `, }, });
export class Story extends React.Component { ... } export default
Relay.createContainer(Story, { fragments: { story: () => Relay.QL` fragment on Story { author { ... } } `, }, }); Colocation
Easy to compose
export class NewsFeed extends Component { … } export default
Relay.createContainer(NewsFeed, { fragments: { feed: () => Relay.QL` fragment on Feed { stories(first: $count) { // fetch viewer's stories edges { // traverse the graph node { ${Story.getFragment('story')} // compose child fragment } } } } `, }, });
class FeedRoute extends Relay.Route { static routeName = "FeedRoute"; static
queries = { feed: () => Relay.QL` query { userFeed(uid: $userID) } `, }; } var feedRoute = new FeedRoute({userID: '123'}); Query Routes
ReactDOM.render( <Relay.RootContainer Component={Feed} route={feedRouter} renderLoading={...} // optional renderFailure={...} // optional
renderFetched={...} // optional />, domElement ); Root Containers
A common problem: Pagination
export default Relay.createContainer(NewsFeed, { initialVariables: { count: 3 }, fragments:
{ feed: () => Relay.QL` fragment on Feed { stories(first: $count) { ... } } `, }, });
class NewsFeed extends React.Component { render() { ... } loadMore()
{ var count = this.props.relay.variables.count; this.props.relay.setVariables({ count: count + 5, }); } }
followers(first: Int, last: String) { edges { node // the
actual follower object, cursor // node identifier in the edge }, pageInfo { hasNextPage, lastCursor } } Pagination: How it works?
export default Relay.createContainer(Story, { fragments: { story: () => Relay.QL`
fragment on Story { author { name followers(first: $limit) @include(if: $isDesktop) { ... } } text } `, }, }); Directives
Mutations
getMutation() { ... } getFatQuery() { ... } getConfigs() {
... } getVariables() { ... } Defining a mutation
export default class LikePostMutation extends Relay.Mutation { getMutation() { ...
} getVariables() { ... } getConfigs() { ... } getFatQuery() { ... } } Defining the mutation
getMutation() { return Relay.QL`mutation{ likePost // `likePost` mutation must be
defined on the server }`; } getMutation()
getVariables() { return { id: this.props.post.postId, like: this.props.like }; }
getVariables()
getConfigs() { return [{ type: 'FIELDS_CHANGE', fieldIDs: { post: this.props.post.id
} }]; } getConfigs()
getFatQuery() { return Relay.QL` fragment on LikePostPayload { post {
id, likesCounter }, } `; } getFatQuery()
export class Story extends React.Component { handleEditPost() { const onFailure
= () => {}; const onSuccess = () => {}; Relay.Store.commitUpdate(new LikePostMutation({ post: this.props.post, like: true }), {onFailure, onSuccess}); } } Sending the mutation
getOptimisticResponse() { return { post: { id: this.props.post.id, likesCounter: this.props.post.likesCounter
+ 1 } } } getOptimisticResponse()
interface Node { id: ID! } type Post extends Node
{ id: ID!, … // post fields } Object Tracking:
- Server-side rendering support - React Router integration - Babel
Plugin - Active development: - Subscription (live) on the work - Client state is being discussed Important Details
- Work well with others tools - Flux, Redux, etc
- Patterns are still emerging Not a silver bullet
Question?