radoslav stankov - handling graphql with react and apollo

136

Upload: fdconf

Post on 28-Jan-2018

57 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 2: Radoslav Stankov - Handling GraphQL with React and Apollo

Radoslav Stankov

@rstankov

http://rstankov.comhttp://github.com/rstankov

Page 3: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 4: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 5: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 6: Radoslav Stankov - Handling GraphQL with React and Apollo

https://speakerdeck.com/rstankov/graphql-with-apollo

Page 7: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 8: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 9: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 10: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 11: Radoslav Stankov - Handling GraphQL with React and Apollo

html

html

json

Page 12: Radoslav Stankov - Handling GraphQL with React and Apollo

json

json

Page 13: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 14: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 15: Radoslav Stankov - Handling GraphQL with React and Apollo

http://graphql.org/

Page 16: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 17: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 18: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 19: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 20: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 21: Radoslav Stankov - Handling GraphQL with React and Apollo

topic { id name description isFollowed image }

Page 22: Radoslav Stankov - Handling GraphQL with React and Apollo

query { topic(id: 1) { id name description isFollowed image }}

Page 23: Radoslav Stankov - Handling GraphQL with React and Apollo

query { topic(id: 1) { id name description isFollowed image }}

POST /graphql

Page 24: Radoslav Stankov - Handling GraphQL with React and Apollo

query { topic(id: 1) { id name description isFollowed image }}

{ "data": { "topic": { "id": 1, "name": "Developer Tools", "description": "Writing code is hard. "isFollowed": true, "image": "assets.producthunt.com/uuid" } }}

POST /graphql

Page 25: Radoslav Stankov - Handling GraphQL with React and Apollo

query { topic(id: 1) { id name description isFollowed image }}

Page 26: Radoslav Stankov - Handling GraphQL with React and Apollo

query { topic(id: 1) { id name description isFollowed image }}

<TopicItem> <TopicImage topic={topic}> <h2>{topic.name}</h2> <p>{topic.description}</p> <TopicFollowButton topic={topic} /> </TopicItem>

Page 27: Radoslav Stankov - Handling GraphQL with React and Apollo

query { topic(id: 1) { id ...TopicItem }}

fragment TopicItem on Topic { id name description ...TopicFollowButton ...TopicImage}

fragment TopicFollowButton on Topic { id name isFollowed}

fragment TopicImage on Topic { image}

<TopicItem> <TopicImage topic={topic}> <h2>{topic.name}</h2> <p>{topic.description}</p> <TopicFollowButton topic={topic} /> </TopicItem>

Page 28: Radoslav Stankov - Handling GraphQL with React and Apollo

query { topic(id: 1) { id ...TopicItem }}

fragment TopicItem on Topic { id name description ...TopicFollowButton ...TopicImage}

fragment TopicFollowButton on Topic { id name isFollowed}

fragment TopicImage on Topic { image}

<TopicItem> <TopicImage topic={topic}> <h2>{topic.name}</h2> <p>{topic.description}</p> <TopicFollowButton topic={topic} /> </TopicItem>

Page 29: Radoslav Stankov - Handling GraphQL with React and Apollo

query { topic(id: 1) { id ...TopicItem }}

fragment TopicItem on Topic { id name description ...TopicFollowButton ...TopicImage}

fragment TopicFollowButton on Topic { id name isFollowed}

fragment TopicImage on Topic { image}

<TopicItem> <TopicImage topic={topic}> <h2>{topic.name}</h2> <p>{topic.description}</p> <TopicFollowButton topic={topic} /> </TopicItem>

Page 30: Radoslav Stankov - Handling GraphQL with React and Apollo

query { topic(id: 1) { id ...TopicItem }}

fragment TopicItem on Topic { id name description ...TopicFollowButton ...TopicImage}

fragment TopicFollowButton on Topic { id name isFollowed}

fragment TopicImage on Topic { image}

<TopicItem> <TopicImage topic={topic}> <h2>{topic.name}</h2> <p>{topic.description}</p> <TopicFollowButton topic={topic} /> </TopicItem>

Page 31: Radoslav Stankov - Handling GraphQL with React and Apollo

query { topic(id: 1) { id ...TopicItem }}

fragment TopicItem on Topic { id name description ...TopicFollowButton ...TopicImage}

fragment TopicFollowButton on Topic { id name isFollowed}

fragment TopicImage on Topic { image}

<TopicItem> <TopicImage topic={topic}> <h2>{topic.name}</h2> <p>{topic.description}</p> <TopicFollowButton topic={topic} /> </TopicItem>

Page 32: Radoslav Stankov - Handling GraphQL with React and Apollo

query { topic(id: 1) { id ...TopicItem }}

fragment TopicItem on Topic { id name description ...TopicFollowButton ...TopicImage}

fragment TopicFollowButton on Topic { id name isFollowed}

fragment TopicImage on Topic { image}

POST /graphql

Page 33: Radoslav Stankov - Handling GraphQL with React and Apollo

query { topic(id: 1) { id ...TopicItem }}

fragment TopicItem on Topic { id name description ...TopicFollowButton ...TopicImage}

fragment TopicFollowButton on Topic { id name isFollowed}

fragment TopicImage on Topic { image}

{ "data": { "topic": { "id": 1, "name": "Developer Tools", "description": "Writing code is hard. "isFollowed": true, "image": "assets.producthunt.com/uuid" } }}

POST /graphql

Page 34: Radoslav Stankov - Handling GraphQL with React and Apollo

query { topic(id: 1) { id ...TopicItem }}

Page 35: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 36: Radoslav Stankov - Handling GraphQL with React and Apollo

query { allTopics { id ...TopicItem }}

Page 37: Radoslav Stankov - Handling GraphQL with React and Apollo

query { allTopics { id ...TopicItem }}

POST /graphql

Page 38: Radoslav Stankov - Handling GraphQL with React and Apollo

{ "data": { "allTopics": [{ "id": 1, "name": "Developer Tools", "description": "Writing code is hard. "isFollowed": true, "image": "assets.producthunt.com/uuid" }, { "id": 2, "name": "Books", "description": "There’s just nothing like a good book. Expand your kn", "isFollowed": false, "image": "assets.producthunt.com/uuid" }, { "id": 3, "name": "iPhone", "description": "The beloved "phone" that changed the", "isFollowed": true, "image": "assets.producthunt.com/uuid" }] }}

query { allTopics { id ...TopicItem }}

POST /graphql

Page 39: Radoslav Stankov - Handling GraphQL with React and Apollo

http://www.apollodata.com/

Page 40: Radoslav Stankov - Handling GraphQL with React and Apollo

/components/TopicFollowButton/Fragment.graphql /components/TopicFollowButton/index.js /components/TopicFollowButton/styles.css/components/TopicImage/Fragment.graphql /components/TopicImage/index.js /components/TopicImage/styles.css/components/TopicItem/Fragment.graphql /components/TopicItem/index.js /components/TopicItem/styles.css /pages/Topics/Query.graphql /pages/Topics/index.js /pages/Topics/styles.css

Page 41: Radoslav Stankov - Handling GraphQL with React and Apollo

/components/TopicFollowButton/Fragment.graphql /components/TopicFollowButton/index.js /components/TopicFollowButton/styles.css/components/TopicImage/Fragment.graphql /components/TopicImage/index.js /components/TopicImage/styles.css/components/TopicItem/Fragment.graphql /components/TopicItem/index.js /components/TopicItem/styles.css /pages/Topics/Query.graphql /pages/Topics/index.js /pages/Topics/styles.css

Page 42: Radoslav Stankov - Handling GraphQL with React and Apollo

/components/TopicFollowButton/Fragment.graphql /components/TopicFollowButton/index.js /components/TopicFollowButton/styles.css/components/TopicImage/Fragment.graphql /components/TopicImage/index.js /components/TopicImage/styles.css/components/TopicItem/Fragment.graphql /components/TopicItem/index.js /components/TopicItem/styles.css /pages/Topics/Query.graphql /pages/Topics/index.js /pages/Topics/styles.css

Page 43: Radoslav Stankov - Handling GraphQL with React and Apollo

fragment TopicImage on Topic { image }

/components/TopicImage/Fragment.graphql

Page 44: Radoslav Stankov - Handling GraphQL with React and Apollo

fragment TopicFollowButton on Topic { id name isFollowed }

/components/TopicFollowButton/Fragment.graphql

Page 45: Radoslav Stankov - Handling GraphQL with React and Apollo

#import "ph/components/TopicFollowButton/Fragment.graphql" #import "ph/components/TopicImage/Fragment.graphql"

fragment TopicItem on Topic { id name slug description ...TopicFollowButton ...TopicImage }

/components/TopicItem/Fragment.graphql

Page 46: Radoslav Stankov - Handling GraphQL with React and Apollo

/components/TopicFollowButton/Fragment.graphql /components/TopicFollowButton/index.js /components/TopicFollowButton/styles.css/components/TopicImage/Fragment.graphql /components/TopicImage/index.js /components/TopicImage/styles.css/components/TopicItem/Fragment.graphql /components/TopicItem/index.js /components/TopicItem/styles.css /pages/Topics/Query.graphql /pages/Topics/index.js /pages/Topics/styles.css

Page 47: Radoslav Stankov - Handling GraphQL with React and Apollo

/components/TopicFollowButton/Fragment.graphql /components/TopicFollowButton/index.js /components/TopicFollowButton/styles.css/components/TopicImage/Fragment.graphql /components/TopicImage/index.js /components/TopicImage/styles.css/components/TopicItem/Fragment.graphql /components/TopicItem/index.js /components/TopicItem/styles.css /pages/Topics/Query.graphql /pages/Topics/index.js /pages/Topics/styles.css

Page 48: Radoslav Stankov - Handling GraphQL with React and Apollo

#import "ph/components/TopicItem/Fragment.graphql"

query TopicsPage { allTopics { id ...TopicItem } }

/pages/Topics/Query.graphql

Page 49: Radoslav Stankov - Handling GraphQL with React and Apollo

TopicPage TopicItem

TopicImage

TopicFollowButton

Page 50: Radoslav Stankov - Handling GraphQL with React and Apollo

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { graphql } from 'react-apollo';

export default function Content(props) { /* ... */ }

export default graphql(QUERY)(Content);

/pages/Topics/index.js

Page 51: Radoslav Stankov - Handling GraphQL with React and Apollo

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { graphql } from 'react-apollo';

export function Content({ data: { allTopics, loading } }) { if (loading) { return <div>Loading...</div>; }

return ( <div> <h1>Topics</h1> <div> {allTopics.map(topic => <TopicItem key={topic.id} topic={topic} />)} </div> </div> ); }

export default graphql(QUERY)(Content);

/pages/Topics/index.js

Page 52: Radoslav Stankov - Handling GraphQL with React and Apollo

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { graphql } from 'react-apollo';

export function Content({ data: { allTopics, loading } }) { if (loading) { return <div>Loading...</div>; }

return ( <div> <h1>Topics</h1> <div> {allTopics.map(topic => <TopicItem key={topic.id} topic={topic} />)} </div> </div> ); }

export default graphql(QUERY)(Content);

/pages/Topics/index.js

Page 53: Radoslav Stankov - Handling GraphQL with React and Apollo

/pages/Topics/index.js

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { graphql } from 'react-apollo'; import withLoading from 'decorators/withLoading';

export function Content({ data: { allTopics } }) { return ( <div> <h1>Topics</h1> <div> {allTopics.map(topic => <TopicItem key={topic.id} topic={topic} />)} </div> </div> ); }

export default graphql(QUERY)(withLoading(Content));

Page 54: Radoslav Stankov - Handling GraphQL with React and Apollo

/pages/Topics/index.js

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { compose, graphql } from 'react-apollo'; import withLoading from 'decorators/withLoading';

export function Content({ data: { allTopics } }) { return ( <div> <h1>Topics</h1> <div> {allTopics.map(topic => <TopicItem key={topic.id} topic={topic} />)} </div> </div> ); }

export default compose( graphql(QUERY), withLoading )(Content);

Page 55: Radoslav Stankov - Handling GraphQL with React and Apollo

/pages/Topics/index.js

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { compose, graphql } from 'react-apollo'; import withLoading from 'decorators/withLoading';

export function Content({ data: { allTopics } }) { return ( <div> <h1>Topics</h1> <div> {allTopics.map(topic => <TopicItem key={topic.id} topic={topic} />)} </div> </div> ); }

export default compose( graphql(QUERY), withLoading )(Content);

Page 56: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 57: Radoslav Stankov - Handling GraphQL with React and Apollo

https://facebook.github.io/relay/

Page 58: Radoslav Stankov - Handling GraphQL with React and Apollo

! Node interface

" Connections

# Mutations

GraphQL Relay Specification

https://facebook.github.io/relay/docs/graphql-relay-specification.html

Page 59: Radoslav Stankov - Handling GraphQL with React and Apollo

query { allTopics { id }}

{ "data": { "allTopics": [{ "id": 1 }] }}

https://facebook.github.io/relay/docs/graphql-object-identification.html

Node Interface

Page 60: Radoslav Stankov - Handling GraphQL with React and Apollo

query { allTopics { id }}

{ "data": { "allTopics": [{ "id": base64("Topic:1") }] }}

https://facebook.github.io/relay/docs/graphql-object-identification.html

Node Interface

Page 61: Radoslav Stankov - Handling GraphQL with React and Apollo

query { allTopics { id }}

{ "data": { "allTopics": [{ "id": "VG9waWM6MQ==" }] }}

Node Interface

https://facebook.github.io/relay/docs/graphql-object-identification.html

Page 62: Radoslav Stankov - Handling GraphQL with React and Apollo

query { node(id: "VG9waWM6MQ==") { id }}

{ "data": { "node": { "id": "VG9waWM6MQ==" } }}

Node Interface

https://facebook.github.io/relay/docs/graphql-object-identification.html

Page 63: Radoslav Stankov - Handling GraphQL with React and Apollo

query { node(id: "VG9waWM6MQ==") { id ... on Topic { name } }}

{ "data": { "node": { "id": "VG9waWM6MQ==", "name": "Games" } }}

Node Interface

https://facebook.github.io/relay/docs/graphql-object-identification.html

Page 64: Radoslav Stankov - Handling GraphQL with React and Apollo

query { node(id: "VG9waWM6MQ==") { id ... on Topic { name } ... on User { fullName } }}

{ "data": { "node": { "id": "VG9waWM6MQ==", "name": "Games" } }}

Node Interface

https://facebook.github.io/relay/docs/graphql-object-identification.html

Page 65: Radoslav Stankov - Handling GraphQL with React and Apollo

query TopicsPage { allTopics { id ...TopicItem } }

Connections

https://facebook.github.io/relay/docs/graphql-connections.html

Page 66: Radoslav Stankov - Handling GraphQL with React and Apollo

query TopicsPage($cursor: String) { allTopics(first: 10, after: $cursor) { edges { node { id ...TopicItem } } pageInfo { hasNextPage } } }

Connections

https://facebook.github.io/relay/docs/graphql-connections.html

Page 67: Radoslav Stankov - Handling GraphQL with React and Apollo

query TopicsPage($cursor: String) { allTopics(last: 10, before: $cursor) { edges { node { id ...TopicItem } } pageInfo { hasPreviousPage } } }

Connections

https://facebook.github.io/relay/docs/graphql-connections.html

Page 68: Radoslav Stankov - Handling GraphQL with React and Apollo

[connectionName](first:, after:) { edges { cursor node { [item] } } pageInfo { endCursor startCursor hasPreviousPage hasNextPage } }

https://facebook.github.io/relay/docs/graphql-connections.html

Connections

Page 69: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 70: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 71: Radoslav Stankov - Handling GraphQL with React and Apollo

#import "ph/components/TopicItem/Fragment.graphql"

query TopicsPage { allTopics { id ...TopicItem } }

/pages/Topics/Query.graphql

Page 72: Radoslav Stankov - Handling GraphQL with React and Apollo

#import "ph/components/TopicItem/Fragment.graphql"

query TopicsPage { allTopics(first: 10) { edges { node { id ...TopicItem } } pageInfo { hasNextPage } } }

/pages/Topics/Query.graphql

Page 73: Radoslav Stankov - Handling GraphQL with React and Apollo

/pages/Topics/index.js

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { compose, graphql } from 'react-apollo'; import withLoading from 'decorators/withLoading';

export function Content({ data: { allTopics } }) { return ( <div> <h1>Topics</h1> <div> {allTopics.map(topic => <TopicItem key={topic.id} topic={topic} />)} </div> </div> ); }

export default compose( graphql(QUERY), withLoading )(Content);

Page 74: Radoslav Stankov - Handling GraphQL with React and Apollo

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { compose, graphql } from 'react-apollo'; import withLoading from 'decorators/withLoading';

export function Content({ data }) { const allTopics = data.allTopics.edges.map(({ node }) => node);

return ( <div> <h1>Topics</h1> <div> {allTopics.map(topic => <TopicItem key={topic.id} topic={topic} />)} </div> </div> ); }

export default compose( graphql(QUERY), withLoading )(Content);

/pages/Topics/index.js

Page 75: Radoslav Stankov - Handling GraphQL with React and Apollo

export function extractEdgeNodes(data) { return data.edges.map(({ node }) => node); }

/utils/graphql.js

Page 76: Radoslav Stankov - Handling GraphQL with React and Apollo

export function extractEdgeNodes(data) { if (!data || !data.edges) { return []; }

return data.edges.map(({ node }) => node); }

/utils/graphql.js

Page 77: Radoslav Stankov - Handling GraphQL with React and Apollo

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { compose, graphql } from 'react-apollo'; import withLoading from 'decorators/withLoading'; import { extractEdgeNodes } from 'utils/graphql';

export function Content({ data }) { const allTopics = extractEdgeNodes(data.allTopics);

return ( <div> <h1>Topics</h1> <div> {allTopics.map(topic => <TopicItem key={topic.id} topic={topic} />)} </div> </div> ); }

export default compose( graphql(QUERY), withLoading )(Content);

/pages/Topics/index.js

Page 78: Radoslav Stankov - Handling GraphQL with React and Apollo

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { compose, graphql } from 'react-apollo'; import withLoading from 'decorators/withLoading'; import { extractEdgeNodes } from 'utils/graphql';

export function Content({ data }) { return ( <div> <h1>Topics</h1> <div> {extractEdgeNodes(data.allTopics).map(topic => ( <TopicItem key={topic.id} topic={topic} /> ))} </div> </div> ); }

export default compose( graphql(QUERY), withLoading )(Content);

/pages/Topics/index.js

Page 79: Radoslav Stankov - Handling GraphQL with React and Apollo

#import "ph/components/TopicItem/Fragment.graphql"

query TopicsPage { allTopics(first: 10) { edges { node { id ...TopicItem } } pageInfo { hasNextPage } } }

/pages/Topics/Query.graphql

Page 80: Radoslav Stankov - Handling GraphQL with React and Apollo

#import "ph/components/TopicItem/Fragment.graphql"

query TopicsPage($cursor: String) { allTopics(first: 10, after: $cursor) { edges { node { id ...TopicItem } } pageInfo { endCursor hasNextPage } } }

/pages/Topics/Query.graphql

Page 81: Radoslav Stankov - Handling GraphQL with React and Apollo

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { compose, graphql } from 'react-apollo'; import withLoading from 'decorators/withLoading'; import { extractEdgeNodes } from 'utils/graphql';

export function Content({ data }) { return ( <div> <h1>Topics</h1> <div> {extractEdgeNodes(data.allTopics).map(topic => ( <TopicItem key={topic.id} topic={topic} /> ))} </div> </div> ); }

export default compose( graphql(QUERY), withLoading )(Content);

/pages/Topics/index.js

Page 82: Radoslav Stankov - Handling GraphQL with React and Apollo

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { compose, graphql } from 'react-apollo'; import withLoading from 'decorators/withLoading'; import { extractEdgeNodes } from 'utils/graphql';

export function Content({ data, loadMore }) { const hasNextPage = data.allTopics.pageInfo.hasNextPage;

return ( <div> <h1>Topics</h1> <InfiniteScroll onScroll={loadMore} hasNextPage={hasNextPage}> {extractEdgeNodes(data.allTopics).map(topic => ( <TopicItem key={topic.id} topic={topic} />, ))} </InfiniteScroll> </div> ); }

export default compose( graphql(QUERY, { props: ({ data }) => ({ loadMore: () => { return data.fetchMore({ variables: { cursor: data.allTopics.pageInfo.endCursor,

/pages/Topics/index.js

Page 83: Radoslav Stankov - Handling GraphQL with React and Apollo

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { compose, graphql } from 'react-apollo'; import withLoading from 'decorators/withLoading'; import { extractEdgeNodes } from 'utils/graphql';

export function Content({ data, loadMore }) { const hasNextPage = data.allTopics.pageInfo.hasNextPage;

return ( <div> <h1>Topics</h1> <InfiniteScroll onScroll={loadMore} hasNextPage={hasNextPage}> {extractEdgeNodes(data.allTopics).map(topic => ( <TopicItem key={topic.id} topic={topic} />, ))} </InfiniteScroll> </div> ); }

export default compose( graphql(QUERY, { props: ({ data }) => ({ loadMore: () => { return data.fetchMore({ variables: { cursor: data.allTopics.pageInfo.endCursor,

/pages/Topics/index.js

Page 84: Radoslav Stankov - Handling GraphQL with React and Apollo

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { compose, graphql } from 'react-apollo'; import withLoading from 'decorators/withLoading'; import { extractEdgeNodes } from 'utils/graphql';

export function Content({ data, loadMore }) { const hasNextPage = data.allTopics.pageInfo.hasNextPage;

return ( <div> <h1>Topics</h1> <InfiniteScroll onScroll={loadMore} hasNextPage={hasNextPage}> {extractEdgeNodes(data.allTopics).map(topic => ( <TopicItem key={topic.id} topic={topic} />, ))} </InfiniteScroll> </div> ); }

export default compose( graphql(QUERY, { props: ({ data }) => ({ loadMore: () => { return data.fetchMore({ variables: { cursor: data.allTopics.pageInfo.endCursor,

/pages/Topics/index.js

Page 85: Radoslav Stankov - Handling GraphQL with React and Apollo

<TopicItem key={topic.id} topic={topic} />, ))} </InfiniteScroll> </div> ); }

export default compose( graphql(QUERY, { props: ({ data }) => ({ loadMore: () => { return data.fetchMore({ variables: { cursor: data.allTopics.pageInfo.endCursor, }, updateQuery(previousResult, { fetchMoreResult }) { const connection = fetchMoreResult.allTopics;

return { allTopics: { edges: [...previousResult.allTopics.edges, ...connection.edges], pageInfo: connection.pageInfo, }, }; }, }); }, }), }), withLoading, )(Content);

/pages/Topics/index.js

Page 86: Radoslav Stankov - Handling GraphQL with React and Apollo

<TopicItem key={topic.id} topic={topic} />, ))} </InfiniteScroll> </div> ); }

export default compose( graphql(QUERY, { props: ({ data }) => ({ loadMore: () => { return data.fetchMore({ variables: { cursor: data.allTopics.pageInfo.endCursor, }, updateQuery(previousResult, { fetchMoreResult }) { const connection = fetchMoreResult.allTopics;

return { allTopics: { edges: [...previousResult.allTopics.edges, ...connection.edges], pageInfo: connection.pageInfo, }, }; }, }); }, }), }), withLoading, )(Content);

/pages/Topics/index.js

$

Page 87: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 88: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 89: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 90: Radoslav Stankov - Handling GraphQL with React and Apollo

graphql(QUERY, { props: ({ data }) => ({ loadMore: () => { return data.fetchMore({ variables: { cursor: data.allTopics.pageInfo.endCursor, }, updateQuery(previousResult, { fetchMoreResult }) { const connection = fetchMoreResult.allTopics;

return { allTopics: { edges: [...previousResult.allTopics.edges, ...connection.edges], pageInfo: connection.pageInfo, }, }; }, }); }, }), });

Page 91: Radoslav Stankov - Handling GraphQL with React and Apollo

graphql(QUERY, { props: ({ data }) => ({ loadMore: () => { return data.fetchMore({ variables: { cursor: data.allTopics.pageInfo.endCursor, }, updateQuery(previousResult, { fetchMoreResult }) { const connection = fetchMoreResult.allTopics;

return { allTopics: { edges: [...previousResult.allTopics.edges, ...connection.edges], pageInfo: connection.pageInfo, }, }; }, }); }, }), });

Page 92: Radoslav Stankov - Handling GraphQL with React and Apollo

graphql(QUERY, { props: ({ data }) => ({ loadMore: () => { return data.fetchMore({ variables: { cursor: data[path].pageInfo.endCursor, }, updateQuery(previousResult, { fetchMoreResult }) { const connection = fetchMoreResult[path];

return { [path]: { edges: [...previousResult[path].edges, ...connection.edges], pageInfo: connection.pageInfo, }, }; }, }); }, }), });

Page 93: Radoslav Stankov - Handling GraphQL with React and Apollo

graphql(QUERY, { props: ({ data }) => ({ loadMore: () => { const path = 'allTopics';

return data.fetchMore({ variables: { cursor: data[path].pageInfo.endCursor, }, updateQuery(previousResult, { fetchMoreResult }) { const connection = fetchMoreResult[path];

return { [path]: { edges: [...previousResult[path].edges, ...connection.edges], pageInfo: connection.pageInfo, }, }; }, }); }, }), });

Page 94: Radoslav Stankov - Handling GraphQL with React and Apollo

function loadMore(data, path) { return data.fetchMore({ variables: { cursor: data[path].pageInfo.endCursor, }, updateQuery(previousResult, { fetchMoreResult }) { const connection = fetchMoreResult[path];

return { [path]: { edges: [...previousResult[path].edges, ...connection.edges], pageInfo: connection.pageInfo, }, }; }, }); }

graphql(QUERY, { props: ({ data }) => ({ loadMore: () => loadMore(data, 'allTopics'), }), });

Page 95: Radoslav Stankov - Handling GraphQL with React and Apollo

/pages/Topics/index.js

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { compose, graphql } from 'react-apollo'; import withLoading from 'decorators/withLoading'; import { extractEdgeNodes, loadMore } from 'utils/graphql';

export function Content({ data, loadMore }) { return ( <div> <h1>Topics</h1> <InfiniteScroll onScroll={loadMore} hasNextPage={data.allTopics.pageInfo.hasNextPage} > {extractEdgeNodes(data.allTopics).map(topic => <TopicItem key={topic.id} topic={topic} />, )} </InfiniteScroll> </div> ); }

export default compose( graphql(QUERY, { props: ({ data }) => ({ loadMore: () => loadMore(data, 'allTopics'), }), }), withLoading, )(Content);

Page 96: Radoslav Stankov - Handling GraphQL with React and Apollo

/pages/Topics/index.js

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { compose, graphql } from 'react-apollo'; import withLoading from 'decorators/withLoading'; import { extractEdgeNodes, loadMore } from 'utils/graphql';

export function Content({ data, loadMore }) { return ( <div> <h1>Topics</h1> <InfiniteScroll onScroll={loadMore} hasNextPage={data.allTopics.pageInfo.hasNextPage} > {extractEdgeNodes(data.allTopics).map(topic => <TopicItem key={topic.id} topic={topic} />, )} </InfiniteScroll> </div> ); }

export default compose( graphql(QUERY, { props: ({ data }) => ({ loadMore: () => loadMore(data, 'allTopics'), }), }), withLoading, )(Content);

%

Page 97: Radoslav Stankov - Handling GraphQL with React and Apollo

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { compose, graphql } from 'react-apollo'; import withLoading from 'decorators/withLoading'; import { extractEdgeNodes, loadMore } from 'utils/graphql';

export function Content({ data, loadMore }) { return ( <div> <h1>Topics</h1> <InfiniteScroll onScroll={loadMore} hasNextPage={data.allTopics.pageInfo.hasNextPage} > {extractEdgeNodes(data.allTopics).map(topic => <TopicItem key={topic.id} topic={topic} />, )} </InfiniteScroll> </div> ); }

export default compose( graphql(QUERY, { props: ({ data }) => ({ loadMore: () => loadMore(data, 'allTopics'), }), }), withLoading, )(Content);

/pages/Topics/index.js

Page 98: Radoslav Stankov - Handling GraphQL with React and Apollo

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { compose, graphql } from 'react-apollo'; import withLoading from 'decorators/withLoading'; import { extractEdgeNodes, loadMore } from 'utils/graphql';

export function Content({ data, loadMore }) { return ( <div> <h1>Topics</h1> <InfiniteScroll onScroll={loadMore} hasNextPage={data.allTopics.pageInfo.hasNextPage} > {extractEdgeNodes(data.allTopics).map(topic => <TopicItem key={topic.id} topic={topic} />, )} </InfiniteScroll> </div> ); }

export default compose( graphql(QUERY, { props: ({ data }) => ({ loadMore: () => loadMore(data, 'allTopics'), }), }), withLoading, )(Content);

&

/pages/Topics/index.js

Page 99: Radoslav Stankov - Handling GraphQL with React and Apollo

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { compose, graphql } from 'react-apollo'; import withLoading from 'decorators/withLoading'; import { extractEdgeNodes } from 'utils/graphql';

export function Content({ data }) { return ( <div> <h1>Topics</h1> <InfiniteScroll data={data} connectionPath="allTopics"> {extractEdgeNodes(data.allTopics).map(topic => <TopicItem key={topic.id} topic={topic} />, )} </InfiniteScroll> </div> ); }

export default compose( graphql(QUERY), withLoading, )(Content);

/pages/Topics/index.js

Page 100: Radoslav Stankov - Handling GraphQL with React and Apollo

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { compose, graphql } from 'react-apollo'; import withLoading from 'decorators/withLoading'; import { extractEdgeNodes } from 'utils/graphql';

export function Content({ data }) { return ( <div> <h1>Topics</h1> <InfiniteScroll data={data} connectionPath="allTopics"> {extractEdgeNodes(data.allTopics).map(topic => <TopicItem key={topic.id} topic={topic} />, )} </InfiniteScroll> </div> ); }

export default compose( graphql(QUERY), withLoading, )(Content);

/pages/Topics/index.js

Page 101: Radoslav Stankov - Handling GraphQL with React and Apollo

import TopicItem from 'components/TopicItem'; import QUERY from './Query.graphql'; import { compose, graphql } from 'react-apollo'; import withLoading from 'decorators/withLoading'; import { extractEdgeNodes } from 'utils/graphql';

export function Content({ data }) { return ( <div> <h1>Topics</h1> <InfiniteScroll data={data} connectionPath="allTopics"> {extractEdgeNodes(data.allTopics).map(topic => <TopicItem key={topic.id} topic={topic} />, )} </InfiniteScroll> </div> ); }

export default compose( graphql(QUERY), withLoading, )(Content);

'

/pages/Topics/index.js

Page 102: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 103: Radoslav Stankov - Handling GraphQL with React and Apollo

Mutations

Page 104: Radoslav Stankov - Handling GraphQL with React and Apollo

! Node interface

" Connections

# Mutations

GraphQL Relay Specification

https://facebook.github.io/relay/docs/graphql-relay-specification.html

Page 105: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 106: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 107: Radoslav Stankov - Handling GraphQL with React and Apollo

mutation FollowTopic($id: ID!) { followTopic(id: $id) { id isFollowed }}

{ "data": { "followTopic": { "id": "VG9waWM6MQ==", "isFollowed": true } }}

POST /graphql

Page 108: Radoslav Stankov - Handling GraphQL with React and Apollo

mutation UnfollowTopic($id: ID!) { unfollowTopic(id: $id) { id isFollowed }}

{ "data": { "unfollowTopic": { "id": "VG9waWM6MQ==", "isFollowed": false } }}

POST /graphql

Page 109: Radoslav Stankov - Handling GraphQL with React and Apollo

mutation FollowTopic($id: ID!) { followTopic(id: $id) { id isFollowed }}

{ "data": { "followTopic": { "id": "VG9waWM6MQ==", "isFollowed": true } }}

POST /graphql

Page 110: Radoslav Stankov - Handling GraphQL with React and Apollo

mutation FollowTopic($id: ID!, $a: Int, $b: Int, $c: Int, $d: Int) { followTopic(id: $id, a: $a, b: $b, c: $c, d: $d) { id isFollowed }}

POST /graphql

Page 111: Radoslav Stankov - Handling GraphQL with React and Apollo

mutation FollowTopic($input: FollowInput!) { followTopic(input: $input) { clientMutationId topic { id isFollowed } }}

https://facebook.github.io/relay/docs/graphql-mutations.html

POST /graphql

Page 112: Radoslav Stankov - Handling GraphQL with React and Apollo

mutation FollowTopic($input: FollowInput!) { followTopic(input: $input) { clientMutationId topic { id isFollowed } }}

{ "data": { "followTopic": { "clientMutationId": "0", "topic": { "id": "VG9waWM6MQ==", "isFollowed": true } } }}

https://facebook.github.io/relay/docs/graphql-mutations.html

POST /graphql

Page 113: Radoslav Stankov - Handling GraphQL with React and Apollo

mutation FollowTopic($input: FollowInput!) { followTopic(input: $input) { clientMutationId topic { id isFollowed } }}

{ "data": { "followTopic": { "clientMutationId": "0", "topic": { "id": "VG9waWM6MQ==", "isFollowed": true } } }}

https://facebook.github.io/relay/docs/graphql-mutations.html

POST /graphql

Page 114: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 115: Radoslav Stankov - Handling GraphQL with React and Apollo

<TopicItem> <TopicImage topic={topic}> <h2>{topic.name}</h2> <p>{topic.description}</p> <TopicFollowButton topic={topic} /> </TopicItem>

Page 116: Radoslav Stankov - Handling GraphQL with React and Apollo

mutation FollowTopic($input: FollowTopicInput!) { followTopic(input: $input) { topic { id isFollowed } } }

/components/FollowButton/FollowTopic.graphql

Page 117: Radoslav Stankov - Handling GraphQL with React and Apollo

mutation UnfollowTopic($input: UnfollowTopicInput!) { unfollowTopic(input: $input) { topic { id isFollowed } } }

/components/FollowButton/UnfollowTopic.graphql

Page 118: Radoslav Stankov - Handling GraphQL with React and Apollo

import CREATE_MUTATION from './FollowTopic.graphql'; import REMOVE_MUTATION from './UnfollowTopic.graphql'; export function FollowButton({ /* ... */ }) { /* ... */ } export default compose( graphql(CREATE_MUTATION, { props: ({ ownProps: { topic: { id } }, mutate }) => ({ followTopic() { return mutate({ variables: { input: { id }, }, }); }, }), }), graphql(REMOVE_MUTATION, { /* ... */ }), )(FollowButton)

/components/FollowButton/index.js

Page 119: Radoslav Stankov - Handling GraphQL with React and Apollo

import CREATE_MUTATION from './FollowTopic.graphql'; import REMOVE_MUTATION from './UnfollowTopic.graphql'; export function FollowButton({ /* ... */ }) { /* ... */ } export default compose( graphql(CREATE_MUTATION, { props: ({ ownProps: { topic: { id } }, mutate }) => ({ followTopic() { return mutate({ variables: { input: { id }, }, optimisticResponse: { response: { node: { __typename: 'Topic', id, isFollowed: true }, }, } }); }, }), }), graphql(REMOVE_MUTATION, { /* ... */ }), )(FollowButton)

/components/FollowButton/index.js

Page 120: Radoslav Stankov - Handling GraphQL with React and Apollo

import CREATE_MUTATION from './FollowTopic.graphql'; import REMOVE_MUTATION from './UnfollowTopic.graphql'; export function FollowButton({ topic, followTopic, unfollowTopic }) { if (topic.isFollowed) { return ( <Button active={true} onClick={unfollowTopic}> Following </Button> ); }

return ( <Button onClick={followTopic}> Follow </Button> ); } export default compose( graphql(CREATE_MUTATION, { /* ... */ }), graphql(REMOVE_MUTATION, { /* ... */ }), )(FollowButton)

/components/FollowButton/index.js

Page 121: Radoslav Stankov - Handling GraphQL with React and Apollo

mutation FollowTopic($input: FollowInput!) { followTopic(input: $input) { clientMutationId topic { id isFollowed } }}

{ "data": { "followTopic": { "clientMutationId": "0", "topic": { "id": "VG9waWM6MQ==", "isFollowed": true } } }}

https://facebook.github.io/relay/docs/graphql-mutations.html

POST /graphql

Page 122: Radoslav Stankov - Handling GraphQL with React and Apollo

mutation FollowTopic($input: FollowInput!) { followTopic(input: $input) { clientMutationId node { id isFollowed } }}

{ "data": { "followTopic": { "clientMutationId": "0", "node": { "id": "VG9waWM6MQ==", "isFollowed": true } } }}

POST /graphql

Page 123: Radoslav Stankov - Handling GraphQL with React and Apollo

mutation FollowTopic($input: FollowInput!) { followTopic(input: $input) { clientMutationId node { id isFollowed } }}

{ "data": { "followTopic": { "clientMutationId": "0", "node": { "id": "VG9waWM6MQ==", "isFollowed": true } } }}

POST /graphql

Page 124: Radoslav Stankov - Handling GraphQL with React and Apollo

Forms

Page 125: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 126: Radoslav Stankov - Handling GraphQL with React and Apollo

#import "./Fragment.graphql" mutation UpdateSetting($input: SettingInput!) { updateSettings(input: $input) { node { ...SettingsForm } }}

/pages/Settings/Mutation.graphql

Page 127: Radoslav Stankov - Handling GraphQL with React and Apollo

function SettingsPage({ data: { settings }, submit }) { return ( <Form submit={submit} data={settings}> <Box title="My Details"> <Field name="name" /> <Field name="headline" /> <Field name="email" /> <Field name="newsletter" type="select" options={OPTIONS} hint="..." /> <Field name="header" type={HeaderUploader} /> </Box> <Buttons submitText="Update" /> </Form> ); }

export default compose( graphql(QUERY) graphql(MUTATION, { props: ({ ownProps, mutate }) => ({ submit(input) { return mutate({ variables: { input }, }); }, }), }), );

/pages/Settings/index.js

Page 128: Radoslav Stankov - Handling GraphQL with React and Apollo

function SettingsPage({ data: { settings }, submit, onSubmit }) { return ( <Form submit={submit} data={settings} onSubmit={onSubmit}> <Box title="My Details"> <Field name="name" /> <Field name="headline" /> <Field name="email" /> <Field name="newsletter" type="select" options={OPTIONS} hint="..." /> <Field name="header" type={HeaderUploader} /> </Box> <Buttons submitText="Update" /> </Form> ); }

export default compose( graphql(QUERY) graphql(MUTATION, { props: ({ ownProps, mutate }) => ({ submit(input) { return mutate({ variables: { input }, }); }, onSubmit(node) { // handle successful update }, }), }), );

/pages/Settings/index.js

Page 129: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 130: Radoslav Stankov - Handling GraphQL with React and Apollo

mutation UpdateSetting($input: SettingInput!) { updateSettings(input: $input) { node { ...SettingsForm } }}

{ "data": { "updateSettings": { "node": { "...": "...", } } }}

POST /graphql

Page 131: Radoslav Stankov - Handling GraphQL with React and Apollo

mutation FollowTopic($input: FollowTopicInput!) { updateSettings(input: $input) { node { id isFollowed } errors { field messages } }}

POST /graphql

{ "data": { "updateSettings": { "node": { "...": "...", } "errors": [{ "name": ["missing"], "email": ["invalid"], }] } }}

Page 132: Radoslav Stankov - Handling GraphQL with React and Apollo

mutation FollowTopic($input: FollowTopicInput!) { updateSettings(input: $input) { node { id isFollowed } errors { field messages } }}

POST /graphql

{ "data": { "updateSettings": { "node": { "...": "...", } "errors": [{ "name": ["missing"], "email": ["invalid"], }] } }}

Page 133: Radoslav Stankov - Handling GraphQL with React and Apollo

GraphQL Apollo Relay Specification ( Node ) Connections * Mutations+ Data Fetching, Pagination- Mutations . Forms

Recap

Page 134: Radoslav Stankov - Handling GraphQL with React and Apollo
Page 135: Radoslav Stankov - Handling GraphQL with React and Apollo

Thanks /

Page 136: Radoslav Stankov - Handling GraphQL with React and Apollo

https://speakerdeck.com/rstankov/graphql-with-apollo