Slide 1

Slide 1 text

The Use Case of GraphQL Client Relay Which Evolve Along React Akishi Tanno / Ikyu

Slide 2

Slide 2 text

- Software Engineer @ Ikyu - Developing services for restaurants - Web Frontend About Speaker

Slide 3

Slide 3 text

- What is Relay? - Why choose Relay? - What have we achieved with Relay? Contents

Slide 4

Slide 4 text

- What is Relay? - Why choose Relay? - What have we achieved with Relay? Contents

Slide 5

Slide 5 text

- GraphQL Client Library - Use with React Application - Developed by Meta What is Relay?

Slide 6

Slide 6 text

- What is Relay? - Why choose Relay? - What have we achieved with Relay? Contents

Slide 7

Slide 7 text

What product have we adopted?

Slide 8

Slide 8 text

- For Restaurants - Customer & Table Management - Good UX is required - Rich UI - New development

Slide 9

Slide 9 text

Why choose Relay?

Slide 10

Slide 10 text

The beginning was the selection of technology for new product.

Slide 11

Slide 11 text

GraphQL 1.API - Good experience in other products - Many examples of adoption and an extensive ecosystem

Slide 12

Slide 12 text

We've been using Vue.js. -> But we decided to adopt React to gain knowledge of multiple ecosystems. 2. UI Library

Slide 13

Slide 13 text

We want to actively incorporate evolution of React -> We adopted Relay, which is developed by Meta, as well as React 3. GraphQL Client

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

- What is Relay? - Why choose Relay? - What have we achieved with Relay? Contents

Slide 16

Slide 16 text

- Prerequisite - Achievements - Problems - Future prospects What have we achieved with Relay?

Slide 17

Slide 17 text

- Prerequisite - Achievements - Problems - Future prospects What have we achieved with Relay?

Slide 18

Slide 18 text

GraphQL Server Specification Object Identification Connections

Slide 19

Slide 19 text

- Prerequisite - Achievements - Problems - Future prospects What have we achieved with Relay?

Slide 20

Slide 20 text

1. Fragment Colocation 2. Render-as-You-Fetch 3. Type Safety 4. Reduce Requests by Cache 5. Data & View Consistency Achievements

Slide 21

Slide 21 text

1. Fragment Colocation query CustomerDetailQuery($customerId: String!) { customer(customerId: $customerId) { ...CustomerName } } fragment CustomerName on Customer{ name nameKana } { "data": { "customer": { “name”: “一休 太郎", “nameKana”: ”いっきゅう たろう" } } }

Slide 22

Slide 22 text

1. Fragment Colocation const CustomerDetail: React.FC = ({ queryRef }) => { const data = usePreloadedQuery( graphql` query CustomerDetailQuery($customerId: String!) { customer(customerId: $customerId) { ...CustomerName_customer } } `, queryRef ) return ( <> {/* ... */} > ) } const CustomerName: React.FC = ({ customerRef }) => { const data = useFragment( graphql` fragment CustomerName_customer on Customer { name nameKana } `, customerRef ) return ( <> {data.nameKana} {data.name} > ) }

Slide 23

Slide 23 text

1. Fragment Colocation const CustomerDetail = ({ queryRef }) => { const data = usePreloadedQuery( graphql` query CustomerDetailQuery($customerId: String!) { customer(customerId: $customerId) { name # nameKana <- under fetch someData # <- over fetch } } `, queryRef ) return ( <> {/* ... */} > ) } const CustomerName = ({ customer }) => { return ( <> {customer.nameKana} {customer.name} > ) } If not Fragment Colocation… - over / under fetch - can’t partial rendering

Slide 24

Slide 24 text

1. Fragment Colocation const CustomerDetail: React.FC = ({ queryRef }) => { const data = usePreloadedQuery( graphql` query CustomerDetailQuery($customerId: String!) { customer(customerId: $customerId) { ...CustomerName_customer } } `, queryRef ) return ( <> {/* ... */} > ) } const CustomerName: React.FC = ({ customerRef }) => { const data = useFragment( graphql` fragment CustomerName_customer on Customer { name nameKana } `, customerRef ) return ( <> {data.nameKana} {data.name} > ) }

Slide 25

Slide 25 text

2. Render-as-You-Fetch https://blog.logrocket.com/improve-developer-experience-with-react-suspense-in-concurrent-mode/

Slide 26

Slide 26 text

const PageRoot: React.FC = () => { const [queryRef, loadQuery] = useQueryLoader(CustomerDetailConcreateQuery) const handleClick = (customerId) => { loadQuery({ customerId, }) } return ( <> {/* */} > ) } const CustomerDetail: React.FC = ({ queryRef }) => { const data = usePreloadedQuery( graphql` query CustomerDetailQuery($customerId: String!) { customer(customerId: $customerId) { ...CustomerName_customer } } `, queryRef ) return ( <> {/* ... */} > ) } 2. Render-as-You-Fetch

Slide 27

Slide 27 text

// relay.config.js module.exports = { src: './src/', schema: './schema.graphql', artifactDirectory: './src/__generated__', excludes: ['**/.next/**', '**/node_modules/**', '**/schema/**'], language: 'typescript', eagerEsModules: true, } Relay Compiler GraphQL Schema // src/__generated__/CustomerDetailQuery.graphql.ts import { ConcreteRequest, Query } from 'relay-runtime'; import { FragmentRefs } from "relay-runtime"; export type CustomerDetailQuery$variables = { customerId: string; }; export type CustomerDetailQuery$data = { readonly customer: { readonly " $fragmentSpreads": FragmentRefs<"CustomerName_customer">; }; }; export type CustomerDetailQuery = { response: CustomerDetailQuery$data; variables: CustomerDetailQuery$variables; }; const node: ConcreteRequest = (function(){ // ... })(); (node as any).hash = "e26f319624f83248c2025a45c3d4bcaa"; export default node; 3. Type Safety GraphQL Query GraphQL Query GraphQL Query Relay Artifacts Relay Artifacts Relay Artifacts

Slide 28

Slide 28 text

3. Type Safety

Slide 29

Slide 29 text

4. Reduce Requests by Cache

Slide 30

Slide 30 text

5. Data & View Consistency const CustomerNameForm: React.FC = () => { const [commitMutation] = useMutation(graphql` mutation CustomerNameForm_updateCustomerName( $customerId: String! $input: CustomerNameInput! ) { updateCustomerName(customerId: $customerId, input: $input) { customer { name } } } `) const onSubmit = (formData) => { commitMutation({ variables: { customerId: data.customerId, input: { name: formData.name, }, }, }) } //... }

Slide 31

Slide 31 text

- Prerequisite - Achievements - Problems - Future prospects What have we achieved with Relay?

Slide 32

Slide 32 text

1. Undocumented behavior 2. Render-as-you-fetch + Incremental Rendering 3. SSR with Next.js Problems

Slide 33

Slide 33 text

1. Undocumented behavior type Mutation { deleteCustomer( customerId: ID! ): DeleteCustomerPayload! } type DeleteCustomerPayload { # Object representing a deleted customer customer: DeleteCustomer! } type DeletedCustomer { # Global Unique ID of deleted customer id: ID! } type Mutation { deleteCustomer( customerId: ID! ): DeleteCustomerPayload! } type DeleteCustomerPayload { # Deleted customer object customer: Customer! } type Customer implements Node { id: ID! name: String! # ... } NG OK

Slide 34

Slide 34 text

2. Render-as-you-fetch + Incremental Rendering https://blog.logrocket.com/improve-developer-experience-with-react-suspense-in-concurrent-mode/

Slide 35

Slide 35 text

2. Render-as-you-fetch + Incremental Rendering

Slide 36

Slide 36 text

2. Render-as-you-fetch + Incremental Rendering

Slide 37

Slide 37 text

3. SSR with Next.js export const getServerSideProps = async (context) => { const customerId = getCustomerId(context) return { props: { preloadedQueries: { customerDetailQuery: await getPreloadedQuery( CustomerDetailConcreteQuery, { customerId, } ), }, }, } } function Hydrate({ Component, props, }: { Component: NextComponentType props: any }) { const environment = useRelayEnvironment() const transformedProps = useMemo(() => { if (props == null) { return props } const { preloadedQueries, ...otherProps } = props if (preloadedQueries == null) { return props } const queryRefs = {} for (const [queryName, { params, variables, response }] of Object.entries( preloadedQueries ) as any) { environment.getNetwork().responseCache.set(params.id, variables, response) queryRefs[queryName] = { environment, fetchKey: params.id, fetchPolicy: 'store-or-network', isDisposed: false, name: params.name, kind: 'PreloadedQuery', variables, } } return { ...otherProps, queryRefs } }, [environment, props]) return }

Slide 38

Slide 38 text

- Prerequisite - Achievements - Problems - Future prospects What have we achieved with Relay?

Slide 39

Slide 39 text

- Other features of Relay - Follow the evolution of React and ecosystem

Slide 40

Slide 40 text

Conclusion

Slide 41

Slide 41 text

- Relay provides us with High DX and Good UX - The best choice to follow the evolution of React