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

Fragment Composition of GraphQL

Fragment Composition of GraphQL

Avatar for Yosuke Kurami

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" ͰάάΔͱ৭ʑग़͖ͯ·͢