$30 off During Our Annual Pro Sale. View Details »

プロトコル、インターフェースとしてのGraphQL

 プロトコル、インターフェースとしてのGraphQL

宣言的UIの状態管理とアーキテクチャSwiftUIとGraphQLによる実践
https://speakerdeck.com/sonatard/swiftui-graphql

GraphQLの誤解
https://speakerdeck.com/sonatard/rethinking-graphql

GraphQL実践ノウハウ
https://speakerdeck.com/sonatard/graphql-knowhow

sonatard

April 25, 2023
Tweet

More Decks by sonatard

Other Decks in Programming

Transcript

  1. 3 ఏڙαʔϏε w "QQJGZ w 4IPQJGZͷΞϓϦΛ/P$PEFͰϦϦʔε w J04ɺ"OESPJE w 8FC্ͰΞϓϦͷσβΠϯΛมߋՄೳ

    w 7*1 w 4IPQJGZʹॊೈͳձһϓϩάϥϜΛఏڙ w ϙΠϯτɺձһϥϯΫɺϦϫʔυɺ༑ୡ঺հ w ৽αʔϏε w ։ൃத
  2. 4 ٕज़ελοΫ w J04 w 4XJGU6* w "QPMMPJ04 w "OESPJE

    w +FUQBDL$PNQPTF w "QPMMP,PUMJO w 8FC w 5ZQF4DSJQU w "QPMMP w #BDLFOE w (P w HRMHFO w ($1$MPVE3VOͳͲ
  3. 7 (SBQI2-ͱ͸ w جຊ w 0WFSGFUDIͷղফ w ඞཁͳ஋͚ͩΛऔಘ w 6OEFSGFUDIͷղফ

    w ౓ͷϦΫΤετͰඞཁͳ஋Λ͢΂ͯऔಘ w εΩʔϚۦಈ։ൃ w εΩʔϚ͔ΒόοΫΤϯυͱΫϥΠΞϯτͷίʔυΛੜ੒ w ୯ҰΤϯυϙΠϯτʹ2VFSZΛૹ৴͢Δ w IUUQTFYNBQMFDPNHSBQIRM
  4. 8 2VFSZ஋ͷऔಘ type Query { viewer: User! } type User

    implements Node { id: ID! order: Order! } type Order implements Node { id: ID! # default஋ΛઃఆͰ͖Δ product(hasStock: Boolean = true): Product! } type Product implements Node { id: ID! name: String! } query GetProduct { viewer { order { product { name } } } } 4DIFNB 2VFSZ
  5. 9 .VUBUJPO࡞੒ɺߋ৽ɺ࡟আͳͲ type Mutation { productCreate(input: ProductInput!): ProductCreatePayload! } input

    ProductInput { name: String! price: String! } type ProductCreatePayload { product: Product } mutation ProductCreate($input: ProductInput!){ productCreate(input: $input) { product { id Name } } } 4DIFNB 2VFSZ
  6. 10 4VCTDSJQUJPOετϦʔϛϯάͰ஋Λऔಘ type Subscription { viewer: User! } type User

    implements Node { id: ID! order: Order! } type Order implements Node { id: ID! product: Product! } type Product implements Node { id: ID! name: String! } subscription GetProduct { viewer { order { product { name } } } } 4DIFNB 2VFSZ
  7. 12 (SBQI2-ͷϝϦοτ w σβΠϯͷϢʔεέʔεʹҾͬுΒΕͯɺ"1*Λมߋ͢Δඞཁ͕ͳ͍ w ΫϥΠΞϯτʮը໘9ʹ:Λදࣔ͢Δඞཁ͕Ͱ͖ͨͷͰɺ9"1*Ͱ:ΛҾ͖͍ͨʯ w 3&45ύλʔϯΫϥΠΞϯτଆ͕ଥڠ͢Δ৔߹ w όοΫΤϯυʮ9"1*ͷ੹຿͔Β:͸֎ΕΔͷͰ:"1*Λ࣮ߦͯ͠ཉ͍͠ʯ

    w ΫϥΠΞϯτʮ9"1*ͱ:"1*ճ࣮ߦ͢Δ͔ʜ355Ͱ69མͪ͠ίʔυ΋ෳࡶʹͳΔͳʯ w 3&45ύλʔϯόοΫΤϯυଆ͕ଥڠ͢Δ৔߹ w όοΫΤϯυʮ9"1*Ͱ:ฦ͢Α͏ʹ͢Δ͔ʜ੹຿ͱͯ͠ҧ࿨ײ͋Δ͠ɺଞͷը໘Ͱ࢖Θͳ͍ͷ ʹ༨ܭͳ3FBE૿͑Δͳɻ΋͘͠͸ઐ༻ͷ9:"1*௥Ճ͢Δ͔ʜʯ w (SBQI2-ͷ৔߹ΫϥΠΞϯτόοΫΤϯυͲͪΒ΋޾ͤ w όοΫΤϯυʮը໘9༻ʹ9ͱ:ΛҾ͘2VFSZΛॻ͍ͯऔಘ͍͍ͯ͠Αଞͷը໘Ͱ͸9͚ͩΛҾ͘ 2VFSZॻ͚͹ຖճ:ΛҾ͘ඞཁͳ͍͔ΒύϑΥʔϚϯεͷ໰୊΋ͳ͍Αʯ w ΫϥΠΞϯτʮ355ͰࡁΉ͠ɺίʔυ͸γϯϓϧɺը໘Ͱඞཁͳ஋͢΂ͯऔಘͰ͖ͯخ͍͠ʯ w όοΫΤϯυΤϯδχΞͱͯ͠ͷ (SBQI2-࠷େͷϝϦοτ
  8. 14 6OJPO union Announcement = TextAnnouncement | URLAnnouncement | ProductAnnouncement

    type TextAnnouncement { id: ID! text: String! } type URLAnnouncement { id: ID! url: String! } type ProductAnnouncement { id: ID! product: Product! } query Announcements { announcements { __typename ... on TextAnnouncement { id text } ... on URLAnnouncement { id url } ... on ProductAnnouncement { id product { id name price } } } } 4DIFNB 2VFSZ type Product { id: ID! name: String! price: Int! }
  9. 15 const Announcements: React.FC = () => { const {

    data } = useQuery(AnnouncementsDocument); return ( <div> <h2>Announcements</h2> <ul> {announcements.map((announcement) => { switch (announcement.__typename) { case "TextAnnouncement": return <li key={announcement.id}>Text: {announcement.text}</li>; case "URLAnnouncement": return ( <li key={announcement.id}> URL: <a href={announcement.url}>{announcement.url}</a> </li> ); case "ProductAnnouncement": return <li key={announcement.id}>Product: {announcement.product.name} - $ {announcement.product.price}</li>; default: return null; } })} </ul> </div> ); }; (SBQI2-Ͱ͸VOJPOͷߏ଄Λద੾ͳܕʹ ஋͕ೖͬͨঢ়ଶͰऔಘͰ͖Δ
  10. 16 *OUFSGBDF interface Announcement { id: ID! title: String! }

    type TextAnnouncement implements Announcement { id: ID! title: String! text: String! } type URLAnnouncement implements Announcement { id: ID! title: String! url: String! } type ProductAnnouncement implements Announcement { id: ID! title: String! product: Product! } query Announcements { announcements { __typename id title ... on TextAnnouncement { text } ... on URLAnnouncement { url } ... on ProductAnnouncement { product { id name price } } } } 4DIFNB 2VFSZ JEͱUJUMF͸JOUFSGBDFʹఆٛ͞Ε͍ͯΔͨΊ ܕʹΑΔ෼ذ͸ෆཁͰɺ ௚઀ར༻͢Δ͜ͱ͕Ͱ͖Δ
  11. 17 const Announcements: React.FC = () => { const {

    data } = useQuery(AnnouncementsDocument); return ( <div> <h2>Announcements</h2> <ul> {announcements.map((announcement) => { const content = (() => { switch (announcement.__typename) { case "TextAnnouncement": return <p>Text: {announcement.text}</p>; case "URLAnnouncement": return ( <p> URL: <a href={announcement.url}>{announcement.url}</a> </p> ); case "ProductAnnouncement": return <p>Product: {announcement.product.name} - ${announcement.product.price}</p>; default: return null; } })(); return ( <li key={announcement.id}> <h3>{announcement.title}</h3> {content} </li> JEͱUJUMF͸JOUFSGBDFʹఆٛ͞Ε͍ͯΔͨΊ ܕʹΑΔ෼ذ͸ෆཁͰɺ ௚઀ར༻͢Δ͜ͱ͕Ͱ͖Δ
  12. 19 2VFSZ!EFGFSEJSFDUJWF query { person(id: "cGVvcGxlOjE=") { ...HomeWorldFragment @defer(label: "homeWorldDefer")

    name } } fragment HomeWorldFragment on Person { homeworld { name } } w !EFGFSEJSFDUJWF͸෇༩ͨ͠2VFSZͷ༏ઌ౓ΛԼ͛Δ w (SBQI2-͸ɺෳ਺ͷ஋Λಉ࣌ʹऔಘͰ͖Δ͕ɺ͢΂ͯͷ݁Ռ͕ἧΘͳ͍ͱϨεϙϯεΛ ฦ͞ͳ͍ɻ!EFGFSEJSFDUJWFΛઃఆ͢Δ͜ͱͰɺ४උ͕Ͱ͖ͨ'JFMEΛઌʹฦ͢͜ͱͰԠ ౴ੑ͕޲্͢Δɻ
  13. 20 2VFSZ!TUSFBNEJSFDUJWF query { person(id: "cGVvcGxlOjE=") { name films @stream(initialCount:

    2, label: "filmsStream") { title } } } w !TUSFBNEJSFDUJWF͸෇༩ͨ͠2VFSZͷ݁ՌΛετϦʔϜͰड͚औΔ w -JTUͷϨεϙϯεΛऔಘ͢Δࡍʹେ͖ͳ-JTUͷ৔߹ʹ!TUSFBNEJSFDUJWFΛࢦఆ͢Δͱ JJUJBM$PVOUͰઃఆͨ͠਺ͷϨεϙϯεΛઌʹड͚औΔ͜ͱͰԠ౴ੑΛ্͛Δɻ
  14. 22 (SBQI2-4FSWFS4QFDJ fi DBUJPO/PEFJOUFSGBDF type Query { node(id: ID!): Node!

    } interface Node { id: ID! @goField(forceResolver: true) } query GetProduct($productID: ID!) { node(id: $productID) { ... on Product { id name } } } w *%Λ࣋ͭΦϒδΣΫτ͸/PEFJOUFSGBDFΛ࣮૷͢Δɻ w ͜ΕΛ࣮૷͢ΔͨΊʹ͸ɺ*%͔ΒͲͷΦϒδΣΫτ͔Λ൑ผͰ͖Δඞཁ͕͋Γ·͢ɻ w (MPCBM0CKFDU*EFOUJ fi DBUJPO w 4IPQJGZͳͲ͸ҎԼͷΑ͏ͳ*%ܗࣜΛ࠾༻͍ͯ͠·͢ɻHJETIPQJGZ1SPEVDU
  15. 23 (SBQI2-$VSTPS$POOFDUJPOT4QFDJ fi DBUJPO w $POOFDUJPO w -JTUܗࣜͷ஋ΛϖʔδωʔγϣϯͰऔಘ͢Δඞཁ͕͋Δ৔߹ͷ4DIFNBఆٛͷఏҊ w 999$POOFDUJPOUZQFΛఆٛ

    w FEHFTPSOPEFTϑΟʔϧυΛ࣋ͭ w QBHF*OGPΛ࣋ͭ w FEHFTͷ৔߹͸ɺ999&EHFΛఆٛ w /PEFϑΟʔϧυΛ࣋ͭ w 1BHF*OGPUZQFΛఆٛ w Χʔιϧͷ։࢝ͱऴྃ஍఺ͷ৘ใ w લޙͷϖʔδωʔγϣϯͷଘࡏͷ༗ແ type UserConnection { pageInfo: PageInfo! edges: [UserEdge!]! nodes: [User!] } type UserEdge { node: User! } type PageInfo { startCursor: String! endCursor: String! hasNextPage: Boolean! hasPreviousPage: Boolean! }
  16. 25 2VFSZɺ.VUBUJPOͷϑΥʔϚοτ w 3FRVFTU w 1045ͷ#PEZ { "query": "...", "operationName":

    "...", "variables": { "myVariable": "someValue", ... } } { "data": { ... }, "errors": [ ... ] } w 3FTQPOTF w #PEZ
  17. 26 2VFSZɺ.VUBUJPOͷϑΥʔϚοτ w 3FRVFTU w (&5ͷRVFSZQBSBNFUFS http://myapi/graphql?query={me{name}} w 1FSTJTUFE2VFSJFT w

    2VFSZΛ4)"ͰϋογϡԽͨ͠΋ͷΛૹ৴ w ϦΫΤεταΠζΛখ͘͞Ͱ͖Δ w (&5ͱར༻͢Δ͜ͱͰ$%/ͰΩϟογϡՄೳʹͳΔ w ొ࿥͞Ε͍ͯΔϋογϡͷΈΛ࣮ߦՄೳʹ͢Δ͜ͱ΋Ͱ͖Δ
  18. 27 4FSJBMJ[BUJPO'PSNBU w (SBQI2-ͷ4FSJBMJ[BUJPO'PSNBU͸+40/ʹݶఆ͞Ε͍ͯͳ͍ w ݱ࣮తͳੈͷதͷ࣮૷ͱͯ͠͸େ൒͕+40/ w .645 w .BQɺ-JTUɺ4USJOHɺ/VMM

    w 4)06-% w #PPMFBOɺ*OUɺ'MPBUɺ&OVN w ରԠ͍ͯ͠ͳ͍৔߹͸4USJOHͱͯ͠ૹΒΕΔ (SBQI2-7BMVF +40/7BMVF .BQ 0CKFDU -JTU "SSBZ /VMM OVMM 4USJOH 4USJOH #PPMFBO USVFPSGBMTF *OU /VNCFS 'MPBU /VNCFS &OVN7BMVF 4USJOH
  19. 29 4DIFNBͷ؅ཧ w (SBQI2-2VFSZͷิ׬΍ίʔυੜ੒ʹ4DIFNB͕ඞཁ w 4DIFNBͷ৘ใΛ֤छπʔϧ͕௚઀ΤϯυϙΠϯτ͔Β*OUSPTQFDUJPO2VFSZʹΑΓ औಘ w 4DIFNBϑΝΠϧΛΤϯυϙΠϯτ͔Β*OUSPTQFDUJPO2VFSZʹΑΓμ΢ϯϩʔυ͠ ͯɺμ΢ϯϩʔυͨ͠ϑΝΠϧΛ֤छπʔϧ͕ಡΈࠐΉ

    w 4DIFNBϑΝΠϧΛϦϙδτϦ͔Βμ΢ϯϩʔυͯ͠ɺμ΢ϯϩʔυͨ͠ϑΝΠϧ Λ֤छπʔϧ͕ಡΈࠐΉ w *OUSPTQFDUJPO2VFSZΛར༻ͨ͠εΩʔϚμ΢ϯϩʔυπʔϧ w IUUQTHJUIVCDPNHRMHPHFUHSBQIRMTDIFNB
  20. 32 'FEFSBUJPO "QPMMP'FEFSBUJPO ɺ4DIFNB4UJUDIJOH w ෳ਺ͷ(SBQI2-"1*Λ૊Έ߹ΘͤͨεʔύʔάϥϑΛ࡞੒͢ΔͨΊͷΞʔΩςΫνϟ w େ͖ͳձࣾͰ͸ɺ(SBQI2-͸(BUFXBZͱͯ͠ѻΘΕͯɺഎޙͷ.JDSPTFSWJDFT΁ͷϦΫ ΤετΛ؅ཧ͢Δɻ w

    .JDSPTFSWJDFT͸νʔϜ୯ҐͰ෼ׂ͞Ε͍ͯΔ͕ɺ(SBQI2-ͷ(BUFXBZ͸ͭͳͷͰଟ͘ ͷνʔϜ͕มߋ͢ΔͨΊσϓϩΠͷͰಠཱੑͳͲ͕೉͘͠ͳΔɻ w ͦ͜Ͱ'FEFSBUJPOΛ࢖͏͜ͱͰɺ(SBQI2-Λ෼ׂ͢Δɻ
  21. 35 1SPUPDPM#V ff FSTPO)551ͱͷൺֱ w εΩʔϚͷදݱ w (SBQIߏ଄ΛදݱͰ͖Δ w 6OJPOɺ*OUFSGBDF͕ඪ४Ͱ࣋ͭͨΊදݱ͠΍͍͢

    w SFTPMWFSʹΑΓϑΟʔϧυͷ௥Ճ͕͠΍͍͢ w طଘͷ2VFSZ͸औಘ͍ͯ͠ͳ͍ͷͰӨڹ͠ͳ͍ w ϑΟʔϧυͷ࡟আͷ͠΍͢͞͸มΘΒͳ͍ w εΩʔϚ؅ཧ w *OUSPTQFDUJPO2VFSZʹΑΓΤϯυϙΠϯτ͔Βμ΢ϯϩʔυPS௚઀ͷࢀর͕Ͱ͖Δ w ੩తղੳπʔϧͷ࣮૷ͷ͠΍͢͞ w ࣮૷͍ͨ͠ݴޠͷ"451BSTFS͕͋Ε͹ࠩ͸ͳ͍
  22. 36 1SPUPDPM#V ff FSTPO)551ͱͷൺֱ w ಠ࣮ࣗ૷ͷ௥Ճ w (SBQI2-EJSFDUJWF w 1SPUPDPM#V

    ff FST1MVHJO w (SBQI2-ͷEJSFDUJWFͷํ͕ؾܰʹ௥ՃͰ͖Δ w .JDSPTFSWJDFT w .JDSPTFSWJDFTؒΛ(SBQI2-Ͱ࣮૷͢ΔͱεΩʔϚઃܭʹۤ࿑͢Δ w .JDSPTFSWJDFT͸H31$ͷࢿ࢈͕ଟ͍ w (SBQI2-͸(BUFXBZʹݶఆͨ͠ํ͕ߟ͑ํ͸γϯϓϧ w 'FEFSBUJPO͸(BUFXBZͷ෼ׂʹ׆༻͢Δ
  23. 37 1SPUPDPM#V ff FSTPO)551ͱͷൺֱ w ϓϩτίϧ΁ͷґଘ w (SBQI2-͸ྑ͘΋ѱ͘΋ଟ͘ͷϓϥΫςΟε͕࢓༷΍ϑϨʔϜϫʔΫʹଘࡏ͢ΔͨΊґ ଘ͕ڧ͍ w

    1SPUPDPM#V ff FSTPO)551͸γϯϓϧͰ͋Γɺଞͷྨࣅϓϩίτϧ΁ͷҠߦ΋༰қ w Ϋϥ΢υαʔϏεͷରԠ w 8FCαʔϏε͕3&45΍+40/Λத৺ʹൃల͖ͯͨ͠ͷͰɺ(SBQI2-ͱϚον͠ͳ͍͜ͱ ͕ଟʑ͋Δ w ྫ ϦΫΤετͷղੳΛΤϯυϙΠϯτ͝ͱʹ΍͍ͬͯΔͱɺ͢΂ͯͷϦΫΤετΛ HSBQIRMΤϯυϙΠϯτʹ౤͛Δ(SBQI2-Ͱ͸෼ੳ͕೉͍͠ w (SBQI2-ઐ༻αʔϏεͷར༻΍ɺطଘαʔϏεͷ্Ͱ(SBQI2-Λద༻ͤ͞ΔͨΊͷ޻෉ ͕ඞཁʹͳΔ w ΞϓϦ w "QPMMPJ04ɺ"QPMMP,PUMJO͕ൃల్্ w (SBQI2-͸1SPUPDPM#V ff FSTΑΓϥΠϒϥϦͷӨڹ͕େ͖͍
  24. 40 ެࣜɾඇެࣜͷ੔ཧ w (SBQI2-4QFD w 2VFSZɺ.VUBUJPOɺ4VCTDSJQUJPO w (SBQI2-4QFD8PSLJOH%SBGU w !EFGFSɺ!TUSFBN

    w 3FMBZ w /PEF w $POOFDUJPO w "QPMMP w 'FEFSBUJPO w 5IF(6*-% w 4DIFNB4UJUIJOH w )BTVSB w GBDUPS"QQ w