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

The Use Case of GraphQL Client Relay Which Evol...

The Use Case of GraphQL Client Relay Which Evolve Along React

Akishi Tanno (Ikyu / Product Development Team, New Business Division, RESZAIKO Business Department / Web Application Engineer, Engineering Manager)

https://tech-verse.me/ja/sessions/221
https://tech-verse.me/en/sessions/221
https://tech-verse.me/ko/sessions/221

Tech-Verse2022

November 18, 2022
Tweet

More Decks by Tech-Verse2022

Other Decks in Technology

Transcript

  1. - What is Relay? - Why choose Relay? - What

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

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

    have we achieved with Relay? Contents
  4. - For Restaurants - Customer & Table Management - Good

    UX is required - Rich UI - New development
  5. GraphQL 1.API - Good experience in other products - Many

    examples of adoption and an extensive ecosystem
  6. We've been using Vue.js. -> But we decided to adopt

    React to gain knowledge of multiple ecosystems. 2. UI Library
  7. We want to actively incorporate evolution of React -> We

    adopted Relay, which is developed by Meta, as well as React 3. GraphQL Client
  8. - What is Relay? - Why choose Relay? - What

    have we achieved with Relay? Contents
  9. 1. Fragment Colocation 2. Render-as-You-Fetch 3. Type Safety 4. Reduce

    Requests by Cache 5. Data & View Consistency Achievements
  10. 1. Fragment Colocation query CustomerDetailQuery($customerId: String!) { customer(customerId: $customerId) {

    ...CustomerName } } fragment CustomerName on Customer{ name nameKana } { "data": { "customer": { “name”: “一休 太郎", “nameKana”: ”いっきゅう たろう" } } }
  11. 1. Fragment Colocation const CustomerDetail: React.FC<Props> = ({ queryRef })

    => { const data = usePreloadedQuery( graphql` query CustomerDetailQuery($customerId: String!) { customer(customerId: $customerId) { ...CustomerName_customer } } `, queryRef ) return ( <> <CustomerName customerRef={data.customer} /> {/* ... */} </> ) } const CustomerName: React.FC<Props> = ({ customerRef }) => { const data = useFragment( graphql` fragment CustomerName_customer on Customer { name nameKana } `, customerRef ) return ( <> <span>{data.nameKana}</span> <span>{data.name}</span> </> ) }
  12. 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 ( <> <CustomerName customer={data.customer} /> {/* ... */} </> ) } const CustomerName = ({ customer }) => { return ( <> <span>{customer.nameKana}</span> <span>{customer.name}</span> </> ) } If not Fragment Colocation… - over / under fetch - can’t partial rendering
  13. 1. Fragment Colocation const CustomerDetail: React.FC<Props> = ({ queryRef })

    => { const data = usePreloadedQuery( graphql` query CustomerDetailQuery($customerId: String!) { customer(customerId: $customerId) { ...CustomerName_customer } } `, queryRef ) return ( <> <CustomerName customerRef={data.customer} /> {/* ... */} </> ) } const CustomerName: React.FC<Props> = ({ customerRef }) => { const data = useFragment( graphql` fragment CustomerName_customer on Customer { name nameKana } `, customerRef ) return ( <> <span>{data.nameKana}</span> <span>{data.name}</span> </> ) }
  14. const PageRoot: React.FC = () => { const [queryRef, loadQuery]

    = useQueryLoader<CustomerDetailQuery>(CustomerDetailConcreateQuery) const handleClick = (customerId) => { loadQuery({ customerId, }) } return ( <> {/* */} <Suspense> <CustomerDetail queryRef={queryRef} /> </Suspense> </> ) } const CustomerDetail: React.FC<Props> = ({ queryRef }) => { const data = usePreloadedQuery( graphql` query CustomerDetailQuery($customerId: String!) { customer(customerId: $customerId) { ...CustomerName_customer } } `, queryRef ) return ( <> <CustomerName customerRef={data.customer} /> {/* ... */} </> ) } 2. Render-as-You-Fetch
  15. // 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
  16. 5. Data & View Consistency const CustomerNameForm: React.FC = ()

    => { const [commitMutation] = useMutation<CustomerNameForm_updateCustomerName>(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, }, }, }) } //... }
  17. 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
  18. 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<NextPageContext, any, any> 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 <Component {...transformedProps} /> }
  19. - Relay provides us with High DX and Good UX

    - The best choice to follow the evolution of React