Slide 1

Slide 1 text

GraphQL ͱ Fragment Colocation @Quramy 2024.04.24 #newt_techtalk

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

ࠓ೔ݴ͍͍ͨ͜ͱ GraphQL ࢖͍ͬͯΔͷʹ Fragment Colocation ͠ͳ͍ͷ͸ଛ

Slide 4

Slide 4 text

ࠓ೔ݴ͍͍ͨ͜ͱ GraphQL ࢖͍ͬͯΔͷʹ Fragment Colocation ͠ͳ͍ͷ͸ଛ

Slide 5

Slide 5 text

ϑϩϯτΤϯυͱσʔλ ϑΣον GraphQL ʹ͍ͭͯߟ͑ΔલʹɺΫϥΠΞϯτ(e.g. Web ϒ ϥ΢β) ͔ΒͷσʔλϑΣονʹ͍ͭͯɺ2௨Γߟ͑ͯΈΔ - ίϯϙʔωϯτͷ௖఺ (Root) ͰσʔλΛऔಘ͢Δ - ίϯϙʔωϯτͷ຤୺ (Leaf) ͰσʔλΛऔಘ͢Δ Root Component Leaf Leaf Leaf

Slide 6

Slide 6 text

ϑϩϯτΤϯυͱσʔλ ϑΣον ίϯϙʔωϯτͷ௖఺(Root) ͰͷσʔλϑΣον - ✅ ΠϯλʔωοτΛލ͍ͩ௨৴ճ਺͕Ұ౓ͰࡁΉͨ ΊɺύϑΥʔϚϯε͕ྑ͍ - 🤮 औಘͨ͠σʔλΛ Leaf ·Ͱ౉͢ඞཁ͕͋Δɻίϯ ϙʔωϯτ௥Ճɾ࡟আ࣌ͷվमൣғ͕େ͖͍ Root Component Leaf Leaf Leaf

Slide 7

Slide 7 text

ϑϩϯτΤϯυͱσʔλ ϑΣον ίϯϙʔωϯτͷ຤୺(Leaf) ͰͷσʔλϑΣον - 🤮 ݸผͷίϯϙʔωϯτ͝ͱʹΠϯλʔωοτӽ͠ ௨৴͕ൃੜ͢Δɻ΢ΥʔλϑΥʔϧ΍ N + 1 Ͱύ ϑΥʔϚϯε͕ྼԽ͍ͯ͘͠ - ✅ ίϯϙʔωϯτͦΕࣗ਎͕ϑΣον͢΂͖σʔλ Λ؅ཧ͍ͯ͠ΔͨΊɺվम࣌ͷӨڹൣғ͸ہॴԽ͞Ε ͍ͯΔ Root Component Leaf Leaf Leaf

Slide 8

Slide 8 text

ϑϩϯτΤϯυͱσʔλ ϑΣον GraphQL ͸͜ͷτϨʔυΦϑΛղফͰ͖Δ 
 (͕ɺGraphQL ΛೖΕ͚ͨͩͰղফ͞ΕΔΘ͚Ͱ͸ͳ͍) τϨʔυΦϑղফʹ͸ Fragment Colocation ͕ඞཁෆՄܽ 3PPUͰσʔλϑΣον -FBGͰσʔλϑΣον 1FSGPSNBODF ✅ 🤮 %FWFMPQFS&YQFSJFODF 🤮 ✅

Slide 9

Slide 9 text

GraphQL ͷਅ਷ GraphQL ͸σʔλϑΣονϓϩηεΛ એݴͱ࣮ߦʹ෼ׂ ͢Δ͜ͱͰͦͷ ਅՁΛൃش͢Δ - ඞཁͱ͢Δσʔλ͸ Leaf ίϯϙʔωϯτʹએݴ͢Δ - ࣮ࡍͷσʔλऔಘ͸ Root ίϯϙʔωϯτͰ࣮ߦ͢Δ Leaf Ͱએݴ͞Εͨ৘ใ (Fragment) ͕ Root ʹू໿͞Εͯ query ͱͳΔ

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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 }) { const user = useFragment(fragment, props.user); return {user.name}; } PopularPosts (Root) PostSummary UserAvatar PostSummary UserAvatar

Slide 12

Slide 12 text

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 }) { const user = useFragment(fragment, props.user); return {user.name}; } ඞཁͱ͢Δσʔλ͸຤୺ࣗ෼ࣗ਎Ͱએݴ͢Δ PopularPosts (Root) PostSummary UserAvatar PostSummary UserAvatar

Slide 13

Slide 13 text

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 }) { const post = useFragment(fragment, props.post); return ( <> {post.title} written by {post.author.name} > ); } PopularPosts (Root) PostSummary PostSummary

Slide 14

Slide 14 text

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 }) { const post = useFragment(fragment, props.post); return ( <> {post.title} written by {post.author.name} > ); } PopularPosts (Root) PostSummary PostSummary ඞཁͱ͢Δσʔλ͸຤୺ࣗ෼ࣗ਎Ͱએݴ͢Δ

Slide 15

Slide 15 text

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 }) { const post = useFragment(fragment, props.post); return ( <> {post.title} written by {post.author.name} > ); } PopularPosts (Root) PostSummary PostSummary ࢠίϯϙʔωϯτ͕ඞཁͱ͢Δσʔλ͸ 'SBHNFOUΛ௨ͯ͡౉͚ͩ͢ 
 ਌͸ͦͷৄࡉʹ͍ͭͯෆՄ஌

Slide 16

Slide 16 text

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 (
    {data.popularPosts.map(post => (
  • ))}
); } PopularPosts (Root)

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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...) Ͱ΋Ұॹ

Slide 20

Slide 20 text

νΣοΫ؍఺ ίϯϙʔωϯτΛ Colocated ʹอͭͨΊͷݪଇ: ✓ Fragment (·ͨ͸ query / mutation) ͷఆٛ͸ίϯϙʔωϯτίʔυ ্ʹهड़͞Ε͍ͯΔʁ ✓ ࣗ෼ͷࢠίϯϙʔωϯτ͕ඞཁͱ͢Δσʔλ͸ Fragment Spread Ͱ هड़͍ͯ͠Δʁ ✓ Fragment ʹొ৔ͨ͠ϑΟʔϧυΛͦͷίϯϙʔωϯτࣗ਎Ͱফඅ͠ ͍ͯΔʁ

Slide 21

Slide 21 text

όουεϝϧୡ - Fragment Colocation Λߦ͍͑ͯͳ͍ GraphQL ϑϩϯτΤϯυ͸ɺ 
 ύϑΥʔϚϯε / อकੑͷ͍ͣΕ͔ΛଛͶΔϦεΫ͕͋Δ - Quramy ͕ࠓ·Ͱݟ͖ͯͨ GraphQL Prjs Λ௨ͯ͠ɺʮո͍͠ͳɻɻɻʯ Λײ͡ΔϙΠϯτΛൈਮͯ͠঺հ͠·͢

Slide 22

Slide 22 text

όουεϝϧ1 src/queries σΟϨΫτϦ src/queries ͷΑ͏ͳσΟϨΫτϦʹ query ΍ mutation Λ഑ஔ͓ͯ͠Γɺ ίϯϙʔωϯτͱಠཱͯ͠؅ཧ͍ͯ͠Δύλʔϯ - ࣮ࡍʹσʔλΛར༻͍ͯ͠ΔՕॴͱ query ͕཭Ε͍ͯΔͨΊɺDX ͕௿ ͍

Slide 23

Slide 23 text

όουεϝϧ1 src/queries σΟϨΫτϦ ΋ͬͱ࣭͕ѱ͍৔߹ͷ͸ 1ͭͷ query (fragment) Λෳ਺ͷίϯϙʔωϯτ ͔Βڞ༗ͯ͠͠·͏έʔε - ͲͪΒ͔ͷίϯϙʔωϯτͰෆཁͳϑΟʔϧυΛऔಘ͍ͯ͠ΔՄೳੑ ͕ඇৗʹߴ͍ - ͢ͳΘͪɺαʔόʔαΠυͰෆཁͳॲཧΛߦΘ͍ͤͯΔ͜ͱʹͳΔ

Slide 24

Slide 24 text

όουεϝϧ2 Output Type Λ import ͍ͯ͠Δ import type { User } from "@gql/graphql" ͷΑ͏ͳ TypeScript ίʔυ͕ϑ ϩϯτΤϯυͰଟ༻͞Ε͍ͯΔέʔε - ίʔυδΣωϨʔλ͕ग़ྗ͢Δ Schema ͷ Output Type Λࢀরͯ͠Α͍ ͷ͸αʔόʔαΠυ͚ͩ - ʮίϯϙʔωϯτ͕ඞཁͱͳΔσʔλΛએݴ͢Δʯͱ͍͏ߟ͑ํͷਅٯ ͱͳͬͯ͠·͏ - ίϯϙʔωϯτ͕ඞཁͱ͢Δ΋ͷ͸ɺFragment ʹ૬౰͢ΔܕͰ͋ͬ ͯɺSchema ͕ఏڙ͢ΔܕͰ͸ͳ͍

Slide 25

Slide 25 text

όουεϝϧ3 useQuery ͕ଟ༻͞Ε͍ͯΔ Root (Page) ίϯϙʔωϯτͰ͸ͳ͍ՕॴͰ useQuery ͕ͻΐͬ͜Γొ৔͢Δ Α͏ͳέʔε - ʮ Colocation ͸஌͍ͬͯΔ͕ɺࣗ෼ͷ Schema Ͱ͸͏·͘Ͱ͖ͳ͘ ͯɻɻɻʯͱͳ͍ͬͯΔ͜ͱ͕ଟ͍ - ϑϩϯτΤϯυ໨ઢͰ͸ѻ͍ʹ͍͘ Schema ઃܭͱͳ͍ͬͯΔՄೳੑ͕ ߴ͍ - GraphQL ͷྑ͞Λ࠷େݶڗड͢Δ্ͰɺSchema ઃܭͱίϯϙʔωϯτ ͷઃܭ͸੾ͬͯ΋੾Γ཭ͤͳ͍. Schema ઃܭϑϩʔΛݟ௚ͨ͠ํ͕Α͍ ͔΋ʁ

Slide 26

Slide 26 text

·ͱΊ - GraphQL ͷ Fragment Colocation ͸ UX ͱ DX Λཱ྆͢Δखஈ - Colocation ͞Ε͍ͯͳ͚Ε͹ɺͲͪΒ͔Λ٘ਜ਼ʹ͍ͯ͠Δͱ͍͏͜ͱ - Colocate ͠Α͏ʂ

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

Thank you !