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

Fragment Composition of GraphQL

Fragment Composition of GraphQL

Yosuke Kurami

April 24, 2024
Tweet

More Decks by Yosuke Kurami

Other Decks in Programming

Transcript

  1. About me - id: @Quramy (GitHub, X) - ৬छ: Web

    ϑϩϯτΤϯυΤϯδχΞ - Quramy ͱ GraphQL: - GraphQL Tokyo ͱ͍͏ Meet up Λෆఆظ։࠵͍ͯ͠·͢ - ͜͜5 ~ 6೥͸Կ͔͠Βͷ GraphQL ͳҊ݅Λ΍͍ͬͯ·͢ (ຊۀ/෭ۀ) 
 Client Side: @apollo/client + React or Vue 
 Server Side: @apollo/server or graphql-ruby + Rails - ts-graphql-plugin ͱ͍͏ϑϩϯτΤϯυ޲͚ͷπʔϧΛϝϯςφϯε͍ͯ͠·͢ 
 https://github.com/Quramy/ts-graphql-plugin
  2. ϑϩϯτΤϯυͱσʔλ ϑΣον GraphQL ʹ͍ͭͯߟ͑ΔલʹɺΫϥΠΞϯτ(e.g. Web ϒ ϥ΢β) ͔ΒͷσʔλϑΣονʹ͍ͭͯɺ2௨Γߟ͑ͯΈΔ - ίϯϙʔωϯτͷ௖఺

    (Root) ͰσʔλΛऔಘ͢Δ - ίϯϙʔωϯτͷ຤୺ (Leaf) ͰσʔλΛऔಘ͢Δ Root Component Leaf Leaf Leaf
  3. ϑϩϯτΤϯυͱσʔλ ϑΣον ίϯϙʔωϯτͷ௖఺(Root) ͰͷσʔλϑΣον - ✅ ΠϯλʔωοτΛލ͍ͩ௨৴ճ਺͕Ұ౓ͰࡁΉͨ ΊɺύϑΥʔϚϯε͕ྑ͍ - 🤮

    औಘͨ͠σʔλΛ Leaf ·Ͱ౉͢ඞཁ͕͋Δɻίϯ ϙʔωϯτ௥Ճɾ࡟আ࣌ͷվमൣғ͕େ͖͍ Root Component Leaf Leaf Leaf
  4. ϑϩϯτΤϯυͱσʔλ ϑΣον ίϯϙʔωϯτͷ຤୺(Leaf) ͰͷσʔλϑΣον - 🤮 ݸผͷίϯϙʔωϯτ͝ͱʹΠϯλʔωοτӽ͠ ௨৴͕ൃੜ͢Δɻ΢ΥʔλϑΥʔϧ΍ N +

    1 Ͱύ ϑΥʔϚϯε͕ྼԽ͍ͯ͘͠ - ✅ ίϯϙʔωϯτͦΕࣗ਎͕ϑΣον͢΂͖σʔλ Λ؅ཧ͍ͯ͠ΔͨΊɺվम࣌ͷӨڹൣғ͸ہॴԽ͞Ε ͍ͯΔ Root Component Leaf Leaf Leaf
  5. ϑϩϯτΤϯυͱσʔλ ϑΣον GraphQL ͸͜ͷτϨʔυΦϑΛղফͰ͖Δ 
 (͕ɺGraphQL ΛೖΕ͚ͨͩͰղফ͞ΕΔΘ͚Ͱ͸ͳ͍) τϨʔυΦϑղফʹ͸ Fragment Colocation

    ͕ඞཁෆՄܽ 3PPUͰσʔλϑΣον -FBGͰσʔλϑΣον 1FSGPSNBODF ✅ 🤮 %FWFMPQFS&YQFSJFODF 🤮 ✅
  6. GraphQL ͷਅ਷ GraphQL ͸σʔλϑΣονϓϩηεΛ એݴͱ࣮ߦʹ෼ׂ ͢Δ͜ͱͰͦͷ ਅՁΛൃش͢Δ - ඞཁͱ͢Δσʔλ͸ Leaf

    ίϯϙʔωϯτʹએݴ͢Δ - ࣮ࡍͷσʔλऔಘ͸ Root ίϯϙʔωϯτͰ࣮ߦ͢Δ Leaf Ͱએݴ͞Εͨ৘ใ (Fragment) ͕ Root ʹू໿͞Εͯ query ͱͳΔ
  7. Fragment Colocation Fragment Colocation ͕ͲͷΑ͏ʹػೳ͢Δ͔Λྫ୊Ͱߟ͑ͯΈΔ 
 GraphQL ͷαϯϓϧͰ͸͓ೃછΈͷϒϩά౤ߘαʔϏεͰ͢ type Post

    { id: ID! title: String! body: String! author: User! createdAt: String! updatedAt: String! } type User { id: ID! name: String! avatarURL: String! createdAt: String! updatedAt: String! } type Query { popularPosts: [Post!]! } PopularPosts (Root) PostSummary UserAvatar PostSummary UserAvatar
  8. Fragment Colocation Example UserAvatar Component import { graphql, useFragment, type

    FragmentType } from './gql'; const fragment = graphql(` fragment UserAvatar_User on User { name avatarURL } `); export function UserAvatar(props: { user: FragmentType<typeof fragment> }) { const user = useFragment(fragment, props.user); return <img src={user.avatarURL} alt={user.name} />; } PopularPosts (Root) PostSummary UserAvatar PostSummary UserAvatar
  9. Fragment Colocation Example UserAvatar Component import { graphql, useFragment, type

    FragmentType } from './gql'; const fragment = graphql(` fragment UserAvatar_User on User { name avatarURL } `); export function UserAvatar(props: { user: FragmentType<typeof fragment> }) { const user = useFragment(fragment, props.user); return <img src={user.avatarURL} alt={user.name} />; } ඞཁͱ͢Δσʔλ͸຤୺ࣗ෼ࣗ਎Ͱએݴ͢Δ PopularPosts (Root) PostSummary UserAvatar PostSummary UserAvatar
  10. Fragment Colocation Example PostSummary Component import { graphql, useFragment, type

    FragmentType } from './gql'; import { UserAvatar } from './UserAvatar'; const fragment = graphql(` fragment PostSummary_Post on Post { id title author { name ...UserAvatar_User } } `); export function PostSummary(props: { post: FragmentType<typeof fragment> }) { const post = useFragment(fragment, props.post); return ( <> <a href={`/posts/${post.id}`}>{post.title}</a> written by <span>{post.author.name}</span> <UserAvatar user={post.author} /> </> ); } PopularPosts (Root) PostSummary PostSummary
  11. Fragment Colocation Example PostSummary Component import { graphql, useFragment, type

    FragmentType } from './gql'; import { UserAvatar } from './UserAvatar'; const fragment = graphql(` fragment PostSummary_Post on Post { id title author { name ...UserAvatar_User } } `); export function PostSummary(props: { post: FragmentType<typeof fragment> }) { const post = useFragment(fragment, props.post); return ( <> <a href={`/posts/${post.id}`}>{post.title}</a> written by <span>{post.author.name}</span> <UserAvatar user={post.author} /> </> ); } PopularPosts (Root) PostSummary PostSummary ඞཁͱ͢Δσʔλ͸຤୺ࣗ෼ࣗ਎Ͱએݴ͢Δ
  12. Fragment Colocation Example PostSummary Component import { graphql, useFragment, type

    FragmentType } from './gql'; import { UserAvatar } from './UserAvatar'; const fragment = graphql(` fragment PostSummary_Post on Post { id title author { name ...UserAvatar_User } } `); export function PostSummary(props: { post: FragmentType<typeof fragment> }) { const post = useFragment(fragment, props.post); return ( <> <a href={`/posts/${post.id}`}>{post.title}</a> written by <span>{post.author.name}</span> <UserAvatar user={post.author} /> </> ); } PopularPosts (Root) PostSummary PostSummary ࢠίϯϙʔωϯτ͕ඞཁͱ͢Δσʔλ͸ 'SBHNFOUΛ௨ͯ͡౉͚ͩ͢ 
 ਌͸ͦͷৄࡉʹ͍ͭͯෆՄ஌
  13. Fragment Colocation Example PopularPosts Component - ίʔυ import { useSuspenseQuery

    } from '@apollo/client'; import { graphql } from './gql'; import { PostSummary } from './PostSummary'; const query = graphql(` query PopularPosts_Query { popularPosts { id ...PostSummary_Post } } `); export function PopularPosts() { const { data } = useSuspenseQuery(query); return ( <ul> {data.popularPosts.map(post => ( <li key={post.id}> <PostSummary post={post} /> </li> ))} </ul> ); } PopularPosts (Root)
  14. fragment UserAvatar_User on User { name avatarURL } fragment PostSummary_Post

    on Post { id title author { name ...UserAvatar_User } } query PopularPosts_Query { popularPosts { id ...PostSummary_Post } } PopularPosts Component (Root Component) PostSummary Component UserAvatar Component Component Tree Query Composition Colocate Colocate Colocate
  15. fragment UserAvatar_User on User { name avatarURL } fragment PostSummary_Post

    on Post { id title author { name ...UserAvatar_User } } query PopularPosts_Query { popularPosts { id ...PostSummary_Post } } PopularPosts Component (Root Component) PostSummary Component UserAvatar Component Component Tree Query Composition Operation for Root Component Colocate Colocate Colocate
  16. Fragment Colocation Example Note: ্ड़ͷྫ͸ React, Apollo Client, graphql-codegen ͷελοΫΛ૝ఆ

    ͕ͨ͠ɺͲͷϥΠϒϥϦΛར༻͢Δ͔͸ຊ࣭Ͱ͸ͳ͍ - GraphQL Client Λ urql ΍ Relay ʹͯ͠΋੒ཱ͢Δ - graphql-codegen ͕ແ͘ͱ΋ಉ͡Α͏ͳίʔυ͕هड़Մೳ 
 (Apollo Client ΍ Relay ͷ useFragment Ͱ΋େ͖͘͸มΘΓ·ͤΜ) - React.js Ҏ֎ͷ Component ࢤ޲ͳ UI ϑϨʔϜϫʔΫ(e.g. Vue, Qwik, etc...) Ͱ΋Ұॹ
  17. νΣοΫ؍఺ ίϯϙʔωϯτΛ Colocated ʹอͭͨΊͷݪଇ: ✓ Fragment (·ͨ͸ query / mutation)

    ͷఆٛ͸ίϯϙʔωϯτίʔυ ্ʹهड़͞Ε͍ͯΔʁ ✓ ࣗ෼ͷࢠίϯϙʔωϯτ͕ඞཁͱ͢Δσʔλ͸ Fragment Spread Ͱ هड़͍ͯ͠Δʁ ✓ Fragment ʹొ৔ͨ͠ϑΟʔϧυΛͦͷίϯϙʔωϯτࣗ਎Ͱফඅ͠ ͍ͯΔʁ
  18. όουεϝϧୡ - Fragment Colocation Λߦ͍͑ͯͳ͍ GraphQL ϑϩϯτΤϯυ͸ɺ 
 ύϑΥʔϚϯε /

    อकੑͷ͍ͣΕ͔ΛଛͶΔϦεΫ͕͋Δ - Quramy ͕ࠓ·Ͱݟ͖ͯͨ GraphQL Prjs Λ௨ͯ͠ɺʮո͍͠ͳɻɻɻʯ Λײ͡ΔϙΠϯτΛൈਮͯ͠঺հ͠·͢
  19. όουεϝϧ1 src/queries σΟϨΫτϦ ΋ͬͱ࣭͕ѱ͍৔߹ͷ͸ 1ͭͷ query (fragment) Λෳ਺ͷίϯϙʔωϯτ ͔Βڞ༗ͯ͠͠·͏έʔε -

    ͲͪΒ͔ͷίϯϙʔωϯτͰෆཁͳϑΟʔϧυΛऔಘ͍ͯ͠ΔՄೳੑ ͕ඇৗʹߴ͍ - ͢ͳΘͪɺαʔόʔαΠυͰෆཁͳॲཧΛߦΘ͍ͤͯΔ͜ͱʹͳΔ
  20. όουεϝϧ2 Output Type Λ import ͍ͯ͠Δ import type { User

    } from "@gql/graphql" ͷΑ͏ͳ TypeScript ίʔυ͕ϑ ϩϯτΤϯυͰଟ༻͞Ε͍ͯΔέʔε - ίʔυδΣωϨʔλ͕ग़ྗ͢Δ Schema ͷ Output Type Λࢀরͯ͠Α͍ ͷ͸αʔόʔαΠυ͚ͩ - ʮίϯϙʔωϯτ͕ඞཁͱͳΔσʔλΛએݴ͢Δʯͱ͍͏ߟ͑ํͷਅٯ ͱͳͬͯ͠·͏ - ίϯϙʔωϯτ͕ඞཁͱ͢Δ΋ͷ͸ɺFragment ʹ૬౰͢ΔܕͰ͋ͬ ͯɺSchema ͕ఏڙ͢ΔܕͰ͸ͳ͍
  21. όουεϝϧ3 useQuery ͕ଟ༻͞Ε͍ͯΔ Root (Page) ίϯϙʔωϯτͰ͸ͳ͍ՕॴͰ useQuery ͕ͻΐͬ͜Γొ৔͢Δ Α͏ͳέʔε -

    ʮ Colocation ͸஌͍ͬͯΔ͕ɺࣗ෼ͷ Schema Ͱ͸͏·͘Ͱ͖ͳ͘ ͯɻɻɻʯͱͳ͍ͬͯΔ͜ͱ͕ଟ͍ - ϑϩϯτΤϯυ໨ઢͰ͸ѻ͍ʹ͍͘ Schema ઃܭͱͳ͍ͬͯΔՄೳੑ͕ ߴ͍ - GraphQL ͷྑ͞Λ࠷େݶڗड͢Δ্ͰɺSchema ઃܭͱίϯϙʔωϯτ ͷઃܭ͸੾ͬͯ΋੾Γ཭ͤͳ͍. Schema ઃܭϑϩʔΛݟ௚ͨ͠ํ͕Α͍ ͔΋ʁ
  22. ·ͱΊ - GraphQL ͷ Fragment Colocation ͸ UX ͱ DX

    Λཱ྆͢Δखஈ - Colocation ͞Ε͍ͯͳ͚Ε͹ɺͲͪΒ͔Λ٘ਜ਼ʹ͍ͯ͠Δͱ͍͏͜ͱ - Colocate ͠Α͏ʂ
  23. ࢀߟϦϯΫ Fragment Colocation ͷॏཁੑʹ͍ͭͯ: - Relay ͷهࣄ (UX / DX

    ͷτϨʔυΦϑ͕؆ܿʹهࡌ͞Ε͍ͯΔ) 
 https://relay.dev/blog/2023/10/24/how-relay-enables-optimal-data- fetching/ - GraphQL Code Generator ͷهࣄ (Fragment Composition ʹ͍ͭͯ) 
 https://the-guild.dev/blog/unleash-the-power-of-fragments-with- graphql-codegen ্هҎ֎Ͱ΋ "graphql colocation" ͰάάΔͱ৭ʑग़͖ͯ·͢