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

GraphQL実践ノウハウ/graphql-knowhow

sonata
October 06, 2022

 GraphQL実践ノウハウ/graphql-knowhow

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

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

sonata

October 06, 2022
Tweet

More Decks by sonata

Other Decks in Programming

Transcript

  1. 1 (SBQI2-࣮ફϊ΢ϋ΢ (SBQI2-ͷબఆͱ࣮ફ5FDINFFWPM !TPOBUBSEͦͳଠ

  2. Appify Technologies CTO ͦͳଠ @sonatard

  3. 3 "QQJGZ w 4IPQJGZͷΞϓϦΛ/P$PEFͰϦϦʔεͰ͖ΔΔαʔϏε w J04ɺ"OESPJE w J04ɺ"OESPJEɺ8FCɺόοΫΤϯυͰ(SBQI2-Λར༻͍ͯ͠·͢ w ࠾༻

    w IUUQTBQQJGZJODDPNDBSFFS
  4. ͸͡Ίʹ

  5. 5 (SBIQ2-͸ศརͰ͕͢ʜ w (SBQI2-͸ͱͯ΋ศརͰ͢ w ͕ɺཧղ͢Δ͜ͱ͕೉͍͠Ͱ͢ w ࠓ·Ͱͱ͸େ͖͘ҟͳΔύϥμΠϜͰ͋ΔͨΊཧղʹۤ࿑͠·͢ w ͔͠͠ཧղͯ͠ਖ਼͘͠(SBQI2-Λར༻͢Δ͜ͱͰ։ൃޮ཰͸େ෯ʹ্͕Γ·͢ɻ

    ։ൃޮ཰ ཧղ౓ Ұఆͷཧղ౓Λ௒͑Δͱ େ͖͘͸։ൃޮ཰͕޲্͢Δ
  6. 6 (SBIQ2-͸ศརͰ͕͢ʜ w (SBQI2-Λཧղ͢Δͱ͖ʹେ੾ͳ͜ͱ͸6OMFBSOJOH͢Δ͜ͱͰ͢ɻ w ৽ٕज़Λطଘٕज़ͷԆ௕Ͱཧղͯ͠͠·͏ͱɺ৽ٕज़͸ຊདྷͷྗΛൃشͰ͖·ͤΜɻ w (SBQI2-ͱ͍͏໊લ͔ͩΒɺ42-ͱࣅͨ΋ͷʁ w ϑϩϯτΤϯυ͕42-ʹ௚઀ΞΫηε͢ΔͨΊͷ΋ͷʁˠ❌

    w ࠓ·Ͱ͸3&45Ͱ͜͏΍͍ͬͯͨ͜ͱ͸ɺ(SBQI2-Ͱ΋͜͏΍Δͷ͔ͳʁ w (SBQIߏ଄Λ׆͔ͣ͞ʹ3&45ͷΑ͏ʹJEΛฦ٫ͯ͠͠·͏ˠ❌ w 3&45ͷΑ͏ʹϑϩϯτΤϯυ͸ड͚औͬͨܕΛม׵͍ͯ͘͠ˠ❌ w (SBQI2-ͷػೳͷ໨తΛ͔Βཧղ͢Δ͜ͱ͕େ੾Ͱ͢ɻ
  7. 7 (SBIQ2-ͱ͸ w جຊ w 0WFSGFUDIͷղফ w ඞཁͳ஋͚ͩΛΫΤϦʔʹهड़ͯ͠औಘ͢ΔͷͰɺ༨ܭͳϑΟϧʔυͷ஋Λऔಘ͠ ͳ͍ͰࡁΉ w

    6OEFSGFUDIͷղফ w (SBQIߏ଄ʹͳ͍ͬͯΔͨΊɺϦϨʔγϣϯ͕͋Δ஋͸̍౓ʹऔಘ͢Δ͜ͱ͕Ͱ͖ Δ w εΩʔϚۦಈ։ൃ w εΩʔϚΛݩʹόοΫΤϯυͷίʔυͷੜ੒΍ΫϥΠΞϯταΠυͷΫΤϦʔͷ هड़Λߦ͏
  8. εΩʔϚۦಈ։ൃ

  9. 9 εΩʔϚۦಈ։ൃͷϝϦοτ  (SBQI2-4DIFNBΛݩʹؔ࿈͢Δίʔυ͕ੜ੒͢ΔͷͰɺܕͷෆҰக͕ى͖ͳ͍  ࣮૷ྔΛେ෯ʹ࡟ݮͰ͖Δ

  10. 10 εΩʔϚۦಈ։ൃ  (SBQI2-4DIFNBΛݩʹؔ࿈͢Δίʔυ͕ੜ੒͢ΔͷͰɺܕͷෆҰக͕ى͖ͳ͍ w 2VFSZɺΫϥΠΞϯτͷGFUDI͢ΔίʔυɺόοΫΤϯυͷϋϯυϥ SFTPMWFS type Query {

    node(id: ID!): Node! viewer: User! adminViewer: Admin! }
  11. 11 εΩʔϚۦಈ։ൃ w (SBQI2-4DIFNBͷఆٛʹ͠ै͍ ੩తղੳ͕Մೳ w -JOUFSͳͲΛࣗ༝ʹ࡞Δ͜ͱ͕ Ͱ͖Δ w (SBQI2-͸DVTUPNEJSFDUJWFͳͲ

    ͰಠࣗͷϧʔϧΛ෇༩͢Δ͜ͱ͕ Մೳ w ಠࣗϧʔϧʹରͯ͠ͷ੩తղੳ πʔϧ΋࡞Δ͜ͱ͕Ͱ͖Δ w EJSFDUJWFͷষͰޙड़͠·͢
  12. 12 (SBQI2-εΩʔϚʹΑΔίʔυੜ੒ͷར఺ w େ෯ʹ࣮૷ྔͷ࡟ݮ͕Մೳ 6* 6TFDBTF 3FQPTJUPSZ 6* (SBQI2-͔Β͸ 7JFX.PEFM͕ฦͬͯ͘ΔͷͰͳ͠

    ੜ੒͞Εͨ(SBQI2-ΫϥΠΞϯτ ϨΠϠʔ͸6*͚ͩʹͳΓɺ͋ͱ͸ ੜ੒͞Εͨ(SBQI2-ΫϥΠΞϯτ Λݺͼग़͚ͩ͢ʹͳΔ ैདྷͷΞʔΩςΫνϟ (SBQI2-ར༻࣌ͷΞʔΩςΫνϟ
  13. 13 εΩʔϚۦಈ։ൃͷར఺ w େ෯ʹ࣮૷ྔͷ࡟ݮ͕Մೳ @Composable fun RaffleProductVariantsPage(productID: ShopifyApiId) { val

    (data, loading, error) = graphql.query( q = RaffleProductVariantsPageQuery(productID) ) when { loading -> { Spinner() } error != null -> { ErrorDialog(error) } else -> { // UI } } } 2VFSZΛ࣮ߦ͢ΔͷΈ query RaffleProductVariantsPage { ɹ ($productID: ID!) { product(id: $productID) { id variants(first: 250) { nodes { id title availableForSale } } } } (SBQI2-ΫΤϦʔ
  14. 14 (SBQI2-εΩʔϚʹΑΔίʔυੜ੒ͷར఺ w ࣮࣭తʹϓϥοτϑΥʔϜ͝ͱͷڞ௨ԽʹΑΔ࣮૷ͷ࡟ݮͱಉ౳ͷޮՌΛಘΔ w ,..ͳͲͰͷڞ௨Խͱಉ౳ͷޮՌ "OESPJE 6* 6TFDBTF 3FQPTJUPSZ

    J046* σʔλΞΫηεΛڞ௨Խ ͯ͠ɺ࣮૷Λ࡟ݮ ,.. "OESPJE 6* J046* (SBQI2- ΫϥΠΞϯτ (SBQI2- ΫϥΠΞϯτ ίʔυੜ੒ʹΑΓ ࣮૷Λ࡟ݮ ,..ʹΑΔڞ௨Խ͸,PUMJOͰͷ࣮૷͕ඞཁʹͳΔ͕ɺ ࣗಈੜ੒Ͱ͸౓΋࣮૷͕ෆཁͳͷͰɺΑΓޮՌ͕ظ଴Ͱ͖Δ
  15. 15 (SBQI2-εΩʔϚʹΑΔίʔυੜ੒ͷར఺ w ڞ௨Խͤͣʹ࣮૷ྔΛݮΒ͢͜ͱʹΑΔߋͳΔར఺ಠཱੑͷ֬อ "OESPJE 6* J046* (SBQI2- ΫϥΠΞϯτ (SBQI2-

    ΫϥΠΞϯτ J04ͱ"OESPJEͰ ҟͳΔ2VFSZΛॻ͖ ҟͳΔΫϥΠΞϯτΛ ੜ੒Ͱ͖Δ (SBQI2- 2VFSZ (SBQI2- 2VFSZ w ۙ೥.JDSPTFSWJDFTΛத৺ʹ։ൃͷಠཱੑ͕ॏཁࢹ͞Ε͍ͯ·͢ɻ w ڞ௨Խ͸։ൃޮ཰Λ্͛·͕͢ɺݸʑͷ࠷େ։ൃ଎౓Λ཈͑ΔՄೳੑ͕͋Γ·͢ɻ w ྆ํͷϓϥοτϑΥʔϜΛҙ࣮ࣝͯ͠૷͠ͳ͚Ε͹͍͚ͳ͍ w ยํͷ౎߹Ͱɺ΋͏ยํͷ։ൃ͕ਐ·ͳ͍ w ͦͷͨΊ.JDSPTFSWJDFTͰ͸ɺ༷ʑͳ௥Ճͷෛ୲ίετΛड͚ೖΕͨ͏͑ͰݸผͷνʔϜ ͱαʔόΛ༻ҙͯ͠ɺ։ൃ΍σϓϩΠͷಠཱੑΛ֬อ͠·͢ɻ
  16. (SBQI2-εΩʔϚʹΑΔίʔυੜ੒ͷར఺ w ΫϥΠΞϯταΠυͰ΋ಉ༷ʹಠཱੑͷ֬อ͸ॏཁͰ͢ɻ w ҟͳΔϓϥοτϑΥʔϜͰॻ͍͍ͯΔݶΓɺ଍ฒΈΛἧ͑ͣϓϥοτϑΥʔϜ͝ͱʹ࠷ దͳॻ͖ํΛͨ͠ํ͕ݸผͰ͸࠷େ଎౓͕ग़·͢ɻ w (SBQI2-͸ΫϥΠΞϯτ͝ͱʹ2VFSZΛॻ͘͜ͱ͕Ͱ͖Δͷ͕ϝϦοτͰ͢ɻ w ڞ௨Խͯ͠͠·͏ͱ͜ͷϝϦοτΛࣦ͍·͢ɻ

    w 2VFSZͷ౷Ұ͸7JFXʹ΋Өڹ͢ΔͨΊɺ7JFX΋଍ฒΈΛἧ͑Δඞཁ͕Ͱ͖ͯ·͢ɻ w 3&45͸͜ͷΑ͏ͳϝϦοτ͕ݩʑ͋ΔΘ͚Ͱ͸ͳ͍ͷͰɺڞ௨ԽʹΑΔଛࣦ͸গͳ͍ɻ w "QQJGZͰ͸ɺJ04ͱ"OESPJEͷ6*΍(SBQI2-ΫΤϦʔͷ࣮૷͸ಠཱͯ͠ߦͳ͍ͬͯ·͢ɻ 16
  17. 17 (SBQI2-εΩʔϚʹΑΔίʔυੜ੒ͷར఺ w ݸผʹ࣮૷͢ΔσϝϦοτ͸ɺ࢓༷ͷ౷Ұ͕೉͘͠ͳΔ͜ͱͰ͢ɻ w ͲͪΒͷԸܙ΋ड͚͍ͨͱ͖ʹ͸ɺ'MVUUFS΍3FBDU/BUJWFͰ͢΂ͯΛڞ௨Խ͢Δબ୒ࢶ ΋͋Γ·͢ɻ w ͔͠͠΋ͪΖΜ'MVUUFS΍3FBDU/BUJWFʹ΋՝୊͸͋ΔͷͰɺਖ਼ղ͸͋Γ·ͤΜɻ w

    ΞʔΩςΫτͷ࣮ྗͷݟͤॴ💪
  18. w ڞ௨Խ͢Δ͜ͱͰ։ൃޮ཰Λ্͛Δ͜ͱ͕Ͱ͖·͕͢ɺ։ൃͷಠཱੑΛҡ࣋͢Δ͜ͱ΋ ։ൃ଎౓Λ্͛Δ͏͑Ͱ͸େ੾Ͱ͋ΓɺόϥϯεΛऔΔඞཁ͕͋Γ·͢ɻ (SBQI2-Λར༻͢Δͱ"1*ΫϥΠΞϯτͷ఺Ͱ͸ɺͲͪΒͷԸܙ΋ड͚Δ͜ͱ͕ՄೳͰ ͢ɻ 18 (SBQI2-εΩʔϚʹΑΔίʔυੜ੒ͷར఺ ڞ௨ԽʹΑΔ ޮ཰ੑ ಠཱੑʹΑΔ

    ։ൃ଎౓ͷ࠷େԽ
  19. 2VFSZͷεΩʔϚઃܭͱೝূೝՄ

  20. 20 2VFSZͷεΩʔϚઃܭ type Query { viewer: User! order(orderID: ID!): Order!

    product(productID: ID!): Product! } type User implements Node { id: ID! orderID: ID! } type Order implements Node { id: ID! productID: ID! } type Product implements Node { id: ID! name: String! } w 3&45ͷΑ͏ͳεΩʔϚ*%Λฦͯ͠ɺͦͷ*%Ͱ࣍ͷ2VFSZΛ࣮ߦ query GetOrderID { viewer { orderID } } query GetProductIDByOrder($orderID: ID!) { order(orderID: $orderID) { productID } } query GetProduct($productID: ID!) { product(productID: $productID) { name } }
  21. 21 2VFSZͷεΩʔϚઃܭ w 3&45ͷΑ͏ͳεΩʔϚ*%ฦͯ͠ɺͦͷ*%Ͱ࣍ͷ2VFSZΛ࣮ߦ ΫϥΠΞϯτ αʔό (FU0SEFS*% PSEFS*% (FU1SPEVDU*%#Z0SEFS QSPEVDU*%

    (FU1SPEVDU QSPEVDUOBNF ճͷϥ΢ϯυτϦοϓ͕ ͋Γɺ(SBQI2-ͷϝϦο τΛੜ͔͍ͤͯͳ͍
  22. 22 2VFSZͷεΩʔϚઃܭ type Query { 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! } w (SBQIߏ଄Λ׆͔ͨ͠εΩʔϚ w ΦϒδΣΫτΛฦ͢ query GetProduct { viewer { order { product { name } } } } ΫϥΠΞϯτ (FU1SPEVDU 1SPEVDUOBNF αʔό ̍ճͷϥ΢ϯυτϦοϓͰࡁΉ
  23. 23 2VFSZͷεΩʔϚઃܭ type Query { 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! } w 3&45ͷΑ͏ͳεΩʔϚ w (SBQIߏ଄Λ׆͔ͨ͠εΩʔϚ type Query { viewer: User! order(orderID: ID!): Order! product(productID: ID!): Product! } type User implements Node { id: ID! orderID: ID! } type Order implements Node { id: ID! productID: ID! } type Product implements Node { id: ID! name: String! } ଞͷUZQFͷ*%Λฦ͍ͯͨ͠Βٙ͏ *%Λฦ͢ͷͰ͸ͳ͘ ΦϒδΣΫτΛฦ͢
  24. 24 2VFSZͷεΩʔϚઃܭ w (SBQIߏ଄Λ׆͔ͨ͠εΩʔϚ͔Βੜ੒͞ΕͨSFTPMWFSͷ࣮૷ w ਌ͷ*%Λड͚औͬͯɺͦͷ*%ʹैͬͯΦϒδΣΫτΛฦ͢ func (r *queryResolver) Viewer(ctx

    context.Context) (*domain.User, error) { userID := ctxvalue.UserID(ctx) // ೝূͨ͠UserID user, _ := r.app.GetUser(ctx, userID) return user, nil } func (r *userResolver) Order(ctx context.Context, user *domain.User) (*domain.Order, error) { order, _ := r.app.GetOrderByUserID(ctx, user.ID) return order, nil } func (r *orderResolver) Product(ctx context.Context, order *domain.Order) (*domain.Product, error) { product, _ := r.app.GetProductByOrderID(ctx, order.ID) return product, nil } w ೝূ͸NJEEMFXBSFͰ"VUIPSJ[BUJPOϔομΛݩʹߦ͏͜ͱ͸3&45ͱมΘΒͳ͍ w ೝূͷ࣮૷͸লུ͠·͢
  25. 25 2VFSZͷεΩʔϚઃܭ w SFTPMWFSͷߟ͑ํ w ਌ͷ*%Λड͚औͬͯɺͦͷ*%ʹैͬͯΦϒδΣΫτΛฦ͢ w ೝূ৘ใΛݩʹฦ͞ͳ͍͜ͱ͕େ੾ w ೝূ৘ใΛݩʹฦ͢ѱ͍ྫ

    w PSEFSSFTPMWFSͰɺ6TFS͕ೝূ͍ͯ͠Δ*%͔Βฦ͢Α͏ʹ࣮૷ func (r *userResolver) Order(ctx context.Context, user *domain.User) (*domain.Order, error) { userID := ctxvalue.UserID(ctx) // ೝূͨ͠UserID // ਌ͷuser.IDΛ࢖Θͣೝূͨ͠userIDΛ࢖͏ order, _ := r.app.GetOrderByUserID(ctx, userID) return order, nil }
  26. 26 2VFSZͷεΩʔϚઃܭ w ೝূ৘ใΛݩʹฦ͢ѱ͍ྫPSEFSSFTPMWFSͰ͸ɺ6TFS͕ೝূ͍ͯ͠Δ*%͔Βฦ͢Α͏ʹ࣮૷ func (r *userResolver) Order(ctx context.Context, user

    *domain.User) (*domain.Order, error) { // Admin͕͜͜ʹ౸ୡ͢ΔͱUserͱͯ͠ೝূ͍ͯ͠ͳ͍ͨΊΤϥʔʹͳͬͯ͠·͏ userID := ctxvalue.UserID(ctx) // ೝূͨ͠UserID // ਌ͷuser.IDΛ࢖Θͣೝূͨ͠userIDΛ࢖͏ order, _ := r.app.GetOrderByUserID(ctx, userID) return order, nil } type Query { viewer: User! adminViewer: Admin! } type User implements Node { id: ID! order: Order! } type Admin implements Node { id: ID! user: User! } type Order implements Node { id: ID! product: Product! } type Product implements Node { id: ID! name: String! } query GetOrder { adminViewer { user { order { #orderΛҾ͘ͱΤϥʔ id } } } } w "ENJO͸؅ཧऀͳͷͰ6TFSͷ0SEFSΛࢀরͰ͖͍͍ͯ͸͕ͣɺΤϥʔ ʹͳΔɻ
  27. 27 2VFSZͷεΩʔϚઃܭ w ਌ͷ*%Λར༻͢Δਖ਼͍͠ྫ func (r *userResolver) Order(ctx context.Context, user

    *domain.User) (*domain.Order, error) { // ೝূͨ͠userIDͰ͸ͳ͘ɺ਌ͷuser.IDΛ࢖͏ order, _ := r.app.GetOrderByUserID(ctx, user.ID) return order, nil } type Query { viewer: User! adminViewer: Admin! } type User implements Node { id: ID! order: Order! } type Admin implements Node { id: ID! user: User! } type Order implements Node { id: ID! product: Product! } type Product implements Node { id: ID! name: String! } query GetOrder { adminViewer { user { order { #orderΛҾ͍ͯ΋ΤϥʔʹͳΒͳ͍ id } } } } w 6TFSͷೝূ৘ใ͕ෆཁʹͳͬͨͨΊ"ENJO͕ฦ͢͜ͱ͕Ͱ͖Δ
  28. 28 2VFSZͷεΩʔϚઃܭ w (SBQI2-ಛ༗ͷศརͳϙΠϯτ w ΫϥΠΞϯτ౎߹ͷϑΟʔϧυͷ௥Ճ͕༰қ w ྫ͑͹ର৅঎඼Λ͓ؾʹೖΓ͍ͯ͠Δ͔Ͳ͏͔ͷϑΟʔϧυ w 1SPEVDUJT'BWPSJUF

    w ͓ؾʹೖΓঢ়ଶ͸1SPEVDUͱ͸ผͷΦϒδΣΫτ͕؅ཧ͍ͯ͠Δ͕ɺΫϥΠΞϯτ͸ 1SPEVDU͔ΒऔಘͰ͖Δͱ࣮૷͕γϯϓϧʹͳΔɻ w 3&45Ͱ΋1SPEVDUΦϒδΣΫτͷϑΟʔϧυʹ௥Ճ͢Δ͜ͱ͸Ͱ͖Δ͕ɺ1SPEVDUΛ औಘ͢Δϋϯυϥ಺෦Ͱ࣮૷͢Δඞཁ͕͋Δɻ·ͨQSPEVDUऔಘͷͨͼʹඞͣ JT'BWPSJUFʹΞΫηε͢ΔͨΊύϑΥʔϚϯε͕མͪΔɻ w (SBQI2-ͳΒJT'BWPSJUFઐ༻ͷSFTPMWFSΛੜ੒Ͱ͖ΔͷͰطଘ࣮૷ʹӨڹΛ༩͑ͣؾܰʹ ௥ՃՄೳɻ·ͨΫϥΠΞϯτ͸ෆཁͳΒ2VFSZʹ௥Ճ͠ͳ͚Ε͹͍͍ͷͰύϑΥʔϚϯε ΋໰୊ͳ͍ɻ͞Βʹطଘͷ2VFSZ͸JT'BWPSJUFΛҾ͍͍ͯͳ͍ͷͰԼҐޓ׵ੑ΋ؾʹͳΒ ͳ͍ɻ w ͜ͷΑ͏ʹ(SBQI2-͸ΫϥΠΞϯτ౎߹ͷϑΟʔϧυͷϋʔυϧ͕ͱͯ΋௿͍ɻ type Query { 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! // User͕͓ؾʹೖΓ͍ͯ͠Δ͔ isFavorite: Boolean! } // ಠཱͨ͠resolver func (r *productResolver) IsFavorite(ctx context.Context, obj *domain.Product) (bool, error) { userID := ctxvalue.UserID(ctx) isFavorite, _ := r.app.IsFavoriteProductByUserID(ctx, obj.ID, userID) return isFavorite, nil }
  29. 29 2VFSZͷεΩʔϚઃܭ w ઌ΄Ͳೝূ৘ใΛݩʹ஋Λฦ͞ͳ͍ํ͕ྑ͍ͱݴ͕ͬͨɺ QSPEVUJT'BWPSJUFͷΑ͏ʹೝূ৘ใΛݩʹฦͨ͠ํ͕ྑ͍͜ͱ΋ ͋Δ w ௨ৗͷΦϒδΣΫτͷϦϨʔγϣϯʹج͖ͮ஋Λฦ͢৔߹ ͱɺ͜ͷΑ͏ʹΫϥΠΞϯτͷͨΊʹಛผͳϑΟʔϧυΛ༻ ҙͨ͠৔߹ͷҧ͍Λ֮ࣗ͠ɺೝূ৘ใͷར༻͸ޙऀ͚ͩʹݶ

    ఆ͢ΔΑ͏ؾΛ෇͚Δ͜ͱ͕େ੾Ͱ͢ɻ type Query { 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! // User͕͓ؾʹೖΓ͍ͯ͠Δ͔ isFavorite: Boolean! }
  30. 30 2VFSZͷεΩʔϚઃܭ w !SPMFEJSFDUJWFʹΑΔ3PMFͷ੍ݶ w 6TFSೝূΛར༻͍ͯ͠Δ͜ͱ͸࣮૷Λݟͳ ͍ͱΘ͔Βͳ͔͕ͬͨɺΫϥΠΞϯτͷΤ ϯδχΞ͸εΩʔϚͰϑΟʔϧυͷݖݶΛ ֬ೝՄೳʹͳΔɻ w

    "ENJO͔Βͷ࣮ߦ͸JT'BWPSJUFSFTPMWFSͷ࣮ ߦલʹΤϥʔ type Product implements Node { id: ID! name: String! ɹɹ // UserͷΈ࣮ߦͰ͖Δ͜ͱΛεΩʔϚͰදݱ isFavorite: Boolean! @role(roles: [USER]) } # directiveͷఆٛ enum Role { ADMIN USER } directive @role(roles: [Role!]!) on FIELD_DEFINITION // Ҿ਺roles͸isFavoriteʹࢦఆͨ͠roles͕౉ͬͯ͘Δ func (r *Role) Role(ctx context.Context, _ interface{}, next graphql.Resolver, roles []domain.Role) (interface{}, error) { myRoles, _ := ctxvalue.Roles(ctx) // ೝূͨ͠RoleΛऔಘ if !myRoles.HasRole(roles) { // ೝূͨ͠Role͕ϑΟʔϧυʹࢦఆ͞Ε͍ͯͳ͔ͬͨΒΤϥʔ fCtx := graphql.GetFieldContext(ctx) return nil, fmt.Errorf("field: %v, roles want=%v, get=%v", fCtx.Path(), roles, myRoles) } return next(ctx) }
  31. 31 2VFSZͷεΩʔϚઃܭ w 3PMFͷϝϦοτ w ΞΫηε͍ͯ͠Δ3PMFΛεΩʔϚͰදݱ͢Δ͜ͱͰɺεΩʔϚͷϝϯςφϯεੑ͕޲্͢Δ w ྫ͑͹"QQJGZͰ͸ w 6TFS͕ࢦఆ͞Ε͍ͯΔͱΞϓϦ͔ΒΞΫηε͍ͯ͠Δ͜ͱ͕Θ͔Δ

    w ΞϓϦ͸ԼҐޓ׵ੑΛҡ࣋͢Δ͜ͱ͕ඞཁͳͷͰɺJT'BWPSJUFϑΟʔϧυ͸ԼҐޓ׵͕ͳ͍ มߋ΍࡟আ͸Ͱ͖ͳ͍ w "ENJO͸8FCͷ؅ཧը໘͔ΒΞΫηε͍ͯ͠Δ͜ͱ͕Θ͔Δ w 8FC͸αʔόͷมߋͱಉ࣌ʹमਖ਼ΛσϓϩΠ͢Ε͹Α͍ͷͰɺԼҐޓ׵ੑΛकΔඞཁ͕ͳ ͍ɻݹ͍ϑΟʔϧυ͸࡟আ͕Մೳɻ type Product implements Node { id: ID! name: String! ɹɹ // UserͷΈ࣮ߦͰ͖Δ͜ͱΛεΩʔϚͰදݱ isFavorite: Boolean! @role(roles: [USER]) secretID: ID! @role(roles: [Admin]) }
  32. 32 2VFSZͷεΩʔϚઃܭ w 3PMFͷ஫ҙ఺ w 3PMF͸ηΩϡϦςΟରࡦʹ͸ͳΒͳ͍৔߹͕͋Δ w 3PMFΛ͚͍ͭͯΔ͔Βͱ͍ͬͯର৅ϑΟʔϧυΛऔಘ͢Δͷʹೝূ৘ใΛ࢖͍ͬͯΔͱ͸ ݶΒͳ͍ w

    ͦͷ৔߹͸ɺಉҰϩʔϧͷҟͳΔϢʔβ͸ΞΫηεͰ͖ͯ͠·͏ type Product implements Node { id: ID! name: String! secretID: ID! @role(roles: [Admin]) }
  33. 33 ೝՄ w ΦϒδΣΫτ΁ͷQFSNJTTJPOͷ؅ཧ w εΩʔϚͷऔಘ͸ೝূ৘ใΛؚΊͳ͍ํ͕ྑ͍ͱݴ͕ͬͨɺ͜Ε͚ͩͰ͸ηΩϡϦςΟʹ໰୊ ͕͋Δ w ҎԼͷεΩʔϚͰ͸ɺ঎඼ͷϨϏϡʔίϝϯτΛͨ͠6TFSͷ1SJWBDZ৘ใʹ୭Ͱ΋ΞΫηε͢ Δ͜ͱ͕Ͱ͖Δ

    w QSPEVDUSFWJFX$PNNFOUVTFSQSJWBDZFNBJM type User implements Node { id: ID! privacy: UserPrivacy! @role(roles: [USER, ADMIN]) } type UserPrivacy implements Node { id: ID! email: String! } type Product implements Node { id: ID! name: String! reviewComment: ReviewComment! } type ReviewComment implements Node { id: ID! text: String! user: User! @role(roles: [USER, ADMIN]) }
  34. 34 ೝՄ w 1FSNJTTJPOEJSFDUJWF w 1FSNJTTJPOEJSFDUJWFʹ͸ɺ64&3ͰΞΫηεͨ͠ͱ͖ʹ08/&3Ͱͳ͍ͱΤϥʔʹͳΔ͜ͱ͕ એݴ͞Ε͍ͯΔɻ w 3PMF͸ϑΟʔϧυ͕ͩͬͨQFSNJTTJPO͸ܕʹࢦఆ͢ΔɻܕͷΦϒδΣΫτ͕ฦ͞ΕΔͱ͖ʹ ඞͣೝূͷ࣮૷ίʔυ͕࣮ߦ͞ΕΔɻ࣮૷͸লུ͠·͢ɻ

    input Permission { role: PermittedRole! type: PermittedType! } enum PermittedRole { USER ADMIN } enum PermittedType { "USER: ର৅ϦιʔεΛอ࣋͢ΔΞϓϦར༻ऀɻADMIN: ର৅ϦιʔεΛอ࣋͢Δ؅ཧऀɻ" OWNER "USER: ৅Ϧιʔεͱಉ͡ΞϓϦΛར༻͍ͯ͠ΔΞϓϦར༻ऀશһɻADMIN: ର৅Ϧιʔεͱಉ͡ΞϓϦͷ؅ཧऀશһɻ" MEMBER } directive @permission(permissions: [Permission!]!) on FIELD_DEFINITION | OBJECT | INTERFACE type UserPrivacy implements Node @permission(permissions: [{role: USER, type: OWNER}]) { id: ID! email: String! }
  35. 35 ೝՄ w εΩʔϚۦಈ։ൃʹΑΓQFSNJTTJPOEJSFDUJWFΛεΩʔϚͰදݱ͢Δ͜ͱͰɺΫϥΠΞϯταΠ υ͸ೝূ৘ใΛεΩʔϚͰ֬ೝ͢Δ͜ͱ͕Ͱ͖Δɻ w ผ్υΩϡϝϯτͷ࡞੒΍࣮૷ͷத਎Λ֬ೝ͢Δඞཁ͕ͳ͍ɻ w ·ͨೝূͷ࣮૷๨Ε͸஫ҙ͠ͳ͍ͱൃੜͯ͠͠·͏͕ɺ(SBQI2-εΩʔϚΛϨϏϡʔ͢Δ͜ͱͰ อূͰ͖Δɻ

    w ݸผͷ࣮૷ΛϨϏϡʔ͢ΔΑΓ΋҆શੑ͕ߴ͘ɺઃఆՕॴ΋໌֬Ͱ͋Δɻ w ·ͨ(SBQI2-ͷεΩʔϚΛ੩తղੳ͢Δ͜ͱͰɺQFSNJTTJPOEJSFDUJWFͷ͚ͭ๨ΕͳͲΛݕग़͢ Δ͜ͱ΋ՄೳʹͳΔɻ w SFTPMWFSͷ(SBQIߏ଄ʹैͬͨ஋ͷऔಘͱQFSNJTTJPOEJSFDUJWFʹΑΔೝՄͷ࢓૊Έ͕෼཭͢Δ͜ ͱͰɺόοΫΤϯυίʔυͷ։ൃମݧ΋େ෯ʹ޲্͠·͢ɻ w όοΫΤϯυϩδοΫ͸஋ͷऔಘʹूத͢Ε͹ྑ͍ͨΊɻ
  36. 36 ೝՄ w SPMFEJSFDUJWF΍QFSNJTTJPOEJSFDUJWFͷΑ͏ͳ΋ͷ͸ϑϨʔϜϫʔΫʹΑͬͯ͸༧Ί༻ҙ͞Ε͍ͯ Δ৔߹΋͋ΔͷͰௐ΂ͯΈ͍ͯͩ͘͞ɻBVUIEJSFDUJWFͳͲʁ w ·ͣ͸ͦΕΛ࢖ͬͯΈͯɺෆ଍͍ͯ͠Δ৔߹ʹ͸ಠࣗʹ࣮૷Λݕ౼ͯ͠Έ͍ͯͩ͘͞ɻ

  37. 2VFSZͷجຊతͳεΩʔϚઃܭͷଓ͖

  38. 38 2VFSͷεΩʔϚઃܭ w *%͔ΒΦϒδΣΫτΛऔಘ͢Δํ๏ w (SBQI2-Ͱ͸(SBQIߏ଄ΛݩʹΦϒδΣΫτΛऔಘ͢Δͱݴ͍·͕ͨ͠ɺ*%Λࢦఆ͠ ͯΦϒδΣΫτΛฦ͍ͨ͜͠ͱ͕͋Γ·͢ɻ w ྫ͑͹ɺ঎඼Ұཡը໘͸*%͸ෆཁͰ͕͢ɺ͔ͦ͜Β঎඼ৄࡉը໘ʹҠಈͨ͠Β঎඼ *%͔Β঎඼ΛҾ͘ඞཁ͕͋Γ·͢ɻ

    w 3&45ͷͱ͖͸ΦϒδΣΫτ͝ͱʹ"1*Λ༻ҙ͢Δ͜ͱ͕ҰൠతͰͨ͠ɻ w QSPVEVDUJE w (SBQI2-Ͱ͸ΦϒδΣΫτ͝ͱͷΫΤϦʔΛ༻ҙ͠ͳ͍͜ͱ͕ҰൠఆͰ͢ɻ w ୅ΘΓʹOPEFΫΤϦʔΛ༻ҙ͠·͢ɻ w 3FMBZͷ(SBQI2-4FSWFS4QFDJ fi DBUJPOʹఆٛ type Query { // ҎԼͷΑ͏ʹ͢ΔͷͰ͸ͳ͘ product(productID: ID!): Product! order(orderID: ID!): Product! // ҎԼͷΑ͏ʹ͢Δ // product΋order΋nodeͰऔಘ node(id: ID!): Node! }
  39. 39 2VFSͷεΩʔϚઃܭ w OPEFJOUFSGBDF 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 ͜ΕΛ࣮૷͢ΔͨΊʹ͸ɺ*%͔ΒͲͷΦϒδΣΫτ͔Λ൑ผͰ͖Δඞཁ͕͋Γ·͢ɻ w 4IPQJGZͳͲ͸ҎԼͷΑ͏ͳ*%ܗࣜΛ࠾༻͍ͯ͠·͢ɻHJETIPQJGZ1SPEVDU
  40. 40 2VFSͷεΩʔϚઃܭ w BMJBT w OPEFRVFSZΛ࢖༻͚ͨͩͩ͠ͱΫϥΠΞϯτଆͷίʔυੜ੒ͰɺOPEFͱ͍͏ϑΟʔ ϧυʹͳͬͯ͠·͏ͨΊBMJBTΛࢦఆ͢ΔͱQSPEVDUͱͳΓࣗવͳܗʹͳΓ·͢ɻ query GetProduct($productID: ID!)

    { node(id: $productID) { ... on Product { id name } } } query GetProduct($productID: ID!) { product: node(id: $productID) { ... on Product { id name } } }
  41. 41 2VFSͷεΩʔϚઃܭ w VOJPOͱJOUFSGBDF w ͕࣌ؒͳ͍ͨΊޙ೔ࢿྉ௥Ճ͠·͢

  42. 42 2VFSͷεΩʔϚઃܭ w Χʔιϧϕʔεͷϖʔδωʔγϣϯ࢓༷ w 3FMBZDVSTPSDPOOFDUJPOTQFDJ fi DBUJPO w ༷ʑͳͱ͜ΖͰઆ໌͞Ε͍ͯΔͱࢥ͏ͷͰجຊతͳ࿩͸লུ͠·͢ɻ

    w ࢓༷ॻʹΑΔͱ3FMBZʹ͸͍͔ͭ͘ͷ࢓༷͕ଘࡏ͢Δɻ w 3FMBZ$MBTTJD w 3FMBZ-FHBDZ w 3FMBZ.PEFSO w ͔ͦ͠͠ΕͧΕͷઆ໌͕࢓༷ʹॻ͔Ε͍ͯͳ͍ͨΊৄࡉ͸ෆ໌
  43. 43 2VFSͷεΩʔϚઃܭ w چ3FMBZ࢓༷ 3FMBZ$MBTTJD 3FMBZ-FHBDZ  type Query {

    users(first: Int, after: String, last: Int before: String): UserConnection! } type User implements Node { id: ID! } type UserConnection { pageInfo: PageInfo! edges: [UserEdge!]! } type UserEdge { cursor: String! node: User! } type PageInfo { hasNextPage: Boolean! hasPreviousPage: Boolean! }
  44. 44 2VFSͷεΩʔϚઃܭ w 3FMBZ.PEFSOͰ͸ɺ1BHF*OGPʹTUBSU$VSTPSͱFOE$VSTPS͕௥Ճ͞Εͨɻ w 6TFS&EHFͷDVSTPS࢖Θͳ͘ͳͬͨ type PageInfo { startCursor:

    String! endCursor: String! hasNextPage: Boolean! hasPreviousPage: Boolean! } type PageInfo { hasNextPage: Boolean! hasPreviousPage: Boolean! } type UserEdge { // ෆཁ cursor: String! node: User! } 3FMBZ.PEFSO 3FMBZ$MBTTJD
  45. 45 2VFSͷεΩʔϚઃܭ w 3FMBZ.PEFSOͰ͸ɺ6TFS&EHFͷDVSTPS࢖Θͳ͘ͳͬͨ͜ͱͰ৽ͨͳ೿ൊͷ୆಄ w FEHFT࢖ΘͣOPEFTΛ༻ҙ͢Δɻ w ͔͜͠͠Ε͸3FMBZͷTQFDʹ͸ॻ͍͍ͯͳ͍ɻ w 4IPQJGZ"1*΍(SBQI2-"1*͸ͪ͜Β΋ରԠ͍ͯ͠Δɻ

    type UserConnection { pageInfo: PageInfo! edges: [UserEdge!]! // cursor࢖Θͳ͍ͳΒedges͸ෆཁͰ nodes: [User!]! // ͜ΕͰྑ͍ͷͰ͸ʁ } type UserEdge { cursor: String! // ࢖Θͳ͍ node: User! }
  46. 46 2VFSͷεΩʔϚઃܭ w OPEFTͷϝϦοτ w ड͚औͬͨϨεϙϯεΛͦͷ··ର৅ͷΦϒδΣΫτͷ഑ྻͱͯ͠ѻ͏͜ͱ͕Ͱ͖Δɻ w &EHFͩͱ٧Ίସ͑࡞ۀ͕ඞཁɻ w ΫϥΠΞϯτͰ͸ม׵ॲཧΛ͠ͳ͍ͷͰ͋·ΓؾʹͳΒͳ͍͕ɺόοΫΤϯυͰ

    ͸υϝΠϯϩδοΫͷ࣮ݱʹม׵͕͔ܽͤͳ͍ɻ type UserConnection { pageInfo: PageInfo! edges: [UserEdge!]! // cursor࢖Θͳ͍ͳΒedges͸ෆཁͰ nodes: [User!]! // ͜ΕͰྑ͍ͷͰ͸ʁ } type UserEdge { cursor: String! // ࢖Θͳ͍ node: User! }
  47. 47 2VFSͷεΩʔϚઃܭ w OPEFTͷσϝϦοτ w 044͕3FMBZͷ࢓༷ʹࡌ͍ͬͯͳ͍OPEFTʹ͸ରԠ͍ͯ͠ͳ͍ɻ w "QPMMPʹ͸ϖʔδωʔγϣϯͷΩϟογϡ࣮૷Λޮ཰తʹߦ͏ SFMBZ4UZMF1BHJOBUJPOͱ͍͏ϢʔςΟϦςΟؔ਺͕༻ҙ͞Ε͍ͯΔ͕ɺ͜Ε͸ OPEFTʹ͸ରԠ͍ͯ͠ͳ͍ɻ

    w ࢓༷ͱͯ͠໌֬ʹͯ͠΄͍͕͠ଘࡏ͠ͳ͍ɻ w 4IPQJGZ΍(SBQI2-ʹOPEFT͕࠾༻͍ͯ͠Δ͕ɺ'BDFCPPLࣾ಺ͰͲ͏ͳ͍ͬͯΔ͔ Θ͔Βͳ͍ɻ64ͷΤϯδχΞͷதͰޱ఻Ͱ఻Θ͍ͬͯΔงғؾ͕͋Γਏ͍ɻ type UserConnection { pageInfo: PageInfo! edges: [UserEdge!]! // cursor࢖Θͳ͍ͳΒedges͸ෆཁͰ nodes: [User!]! // ͜ΕͰྑ͍ͷͰ͸ʁ } type UserEdge { cursor: String! // ࢖Θͳ͍ node: User! }
  48. 48 2VFSZͷεΩʔϚઃܭͱೝূೝՄ w Χʔιϧϕʔεͷϖʔδωʔγϣϯ·ͱΊ w ݱࡏ͸ɺ3FMBZ.PEFSOʹै͏͜ͱ͕҆શ w ͔͠͠ݱ࣮తʹ͸3FMBZ.PEFSOͷ&EHFTΛ࢖͏͜ͱΑΓ΋OPEFTͷํ͕࣮૷͕γϯϓϧʹͳΔ ͷͰɺͪ͜ΒΛ࠾༻͢Δ͜ͱ΋Մೳ w

    ඇඪ४ͳͷͰҰఆͷϦεΫΛड͚ೖΕΔඞཁ͕͋Δ
  49. 49 2VFSZͷεΩʔϚઃܭͱೝূೝՄ w εΩʔϚઃܭ·ͱΊ w Ҏ্ͷج൫͕੔͍ɺཧղ͕Ͱ͖Δͱ(SBQI2-ͷόοΫΤϯυ։ൃ͸γϯϓϧʹͳΓ·͢ɻ w (SBQIߏ଄ͰΦϒδΣΫτΛฦ͢ w 3&45Ͱ*%Λฦ͍ͯͨ͠ͱ͜ΖΛɺΦϒδΣΫτʹ͢Δ

    w ΦϒδΣΫτؒͷϦϨʔγϣϯΛઃܭ͢Δ͜ͱ͸3&45ͱมΘΒͳ͍ w ஋औಘͷ࣮૷ޙʹSPMFɺQFSNJTTJPOEJSFDUJWFΛઃఆ͢Δ w 3&45ͱ͸ҟͳΓ஋औಘͱೝՄΛ෼཭ͯ͠ɺ஋औಘʹϑΥʔΧεͨ͠γϯϓϧͳ࣮૷͕Մೳ w OPEFɺ$POOFDUJPOʹै͏ w Ұൠతͳ࢓༷͕ఆٛ͞Ε͍ͯΔ
  50. .VUBJUPOͷεΩʔϚઃܭ

  51. 51 .VUBJUPOͷεΩʔϚઃܭ w େ͖͘͸3&45ͱมΘΒͳ͍ w 3&45ΑΓ΋31$Λҙࣝͨ͠ઃܭʹͳΔ w H31$΍+40/31$ͷํ͕ΑΓࣅ͍ͯΔ w (SBQI2-ͷNVUBJUPOͰ͸ͳ͘3&45Ͱ͍͍ͷͰ͸ʁ

    w Ωϟογϡͷߋ৽ͷͨΊʹNVUBUJPO͸ඞཁ w (SBQI2-ͷ༷ʑͳػೳΛ2VFSZͱ౷Ұͯ͠࢖͑ΔͷͰศར w EJSFDUJWFͳͲ
  52. 52 .VUBJUPOͷεΩʔϚઃܭ w *OQVUͱ1BZMPBEܕΛ༻ҙ͢Δ w *OQVUʹ͸UZQFΛ౉͢͜ͱ͸Ͱ͖ͳ͍ w ͜ΕʹΑΓ҆қʹUZQFΛJOQVUͱͯ͠࠶ར༻͢Δ͜ͱΛ๷͍Ͱ͍Δ w ྫ͑͹6TFSͷҰ෦ͷ৘ใΛߋ৽͢ΔNVUBUJPOʹؙ͝ͱ6TFSΛ౉ͯ͠͠·͏ͳͲ

    type Mutation { addFavoriteProduct(input: AddFavoriteProductInput!): AddFavoriteProductPayload! @role(roles: [USER]) } input AddFavoriteProductInput { productID: ID! @id(kind: "Product") } type AddFavoriteProductPayload { product: Product! }
  53. 53 .VUBJUPOͷεΩʔϚઃܭ w 1BZMPBE͸༻ҙͤͣɺ௚઀ର৅ͷΦϒδΣΫτΛฦ͢͜ͱ΋Մೳ͕͓ͩ͢͢Ί͠ͳ͍ɻ w কདྷతʹଞͷϨεϙϯεΛฦͨ͘͠ͳͬͨ৔߹ʹରԠͰ͖ͳ͍ͨΊɻ type Mutation { addFavoriteProduct(input:

    AddFavoriteProductInput!): AddFavoriteProductPayload! @role(roles: [USER]) # ҎԼ΋Մೳ͕͓ͩ͢͢Ί͠ͳ͍ addFavoriteProduct(input: AddFavoriteProductInput!): Product! @role(roles: [USER]) } input AddFavoriteProductInput { productID: ID! @id(kind: "Product") } type AddFavoriteProductPayload { product: Product! }
  54. 54 .VUBJUPOͷεΩʔϚઃܭ w 1BZMPBE͸ɺΫϥΠΞϯτ͕͜ͷNVUBUJPOʹΑΓߋ৽ͨ͠ΦϒδΣΫτΛऔಘͰ͖ΔΑ ͏ʹ͢Δɻ w ΫϥΠΞϯτ͸ɺαʔόͰߋ৽ͨ͠৘ใΛݩʹΫϥΠΞϯτΩϟογϡΛߋ৽͢ΔͨΊ ʹ1BZMPBEΛར༻͢Δɻ w ·ͨNVUBUJPOͷฦΓ஋΋2VFSZʹͳ͍ͬͯΔͷͰɺΫϥΠΞϯτ͸޷͖ͳϨεϙϯε

    ͚ͩऔಘՄೳʹͳ͍ͬͯΔɻ type Mutation { addFavoriteProduct(input: AddFavoriteProductInput!): AddFavoriteProductPayload! } input AddFavoriteProductInput { productID: ID! @id(kind: "Product") } type AddFavoriteProductPayload { product: Product! } mutation AddFavoriteProduct($productID: ID!) { addFavoriteProduct(input: { productID: $productID }) { # QueryͳͷͰ޷͖ͳ஋͚ͩऔಘՄೳ product { id name # ΫϥΠΞϯτ͸isFavoriteͷมߋͨͨ͠ΊΩϟογϡߋ৽Λ͢Δɻ isFavorite } } }
  55. 55 .VUBJUPOͷεΩʔϚઃܭ w ࣮͸ΫϥΠΞϯτΩϟογϡͷࢹ఺ͰɺҎԼͷNVUBUJPO͸׬ᘳͱ͸ݴ͑ͳ͍ɻ w ΋͠6TFSʹ͸GBWPSJUF1SPEVDUTΫΤϦʔͱ͍͏6TFSͷ͓ؾʹೖΓ঎඼ҰཡΛऔಘ͢ΔΫ ΤϦʔ͕ଘࡏ͢Δ৔߹ w BEE'BWPSJUF1SPEVDU࣮ߦͨ͠ͱͯ͠΋ɺ1BZMPBEͰGBWPSJUF1SPEVDUTΫΤϦʔΛऔಘ͠ ͍ͯͳ͍ͨΊΩϟογϡߋ৽͕͞Εͳ͍ɻ

    type User implements Node { id: ID! favoriteProducts: [Product!]! } mutation AddFavoriteProduct($productID: ID!) { addFavoriteProduct(input: { productID: $productID }) { product { id name # ΫϥΠΞϯτ͸isFavoriteͷมߋͨͨ͠ΊΩϟογϡߋ৽Λ͢Δɻ isFavorite } } }
  56. 56 .VUBJUPOͷεΩʔϚઃܭ w 1BZMPBEͰGBWPSJUF1SPEVDUTΫΤϦʔΛऔಘ͍ͯ͠ͳ͍ͨΊΩϟογϡߋ৽͕͞Εͳ͍໰ ୊ͷղܾࡦ w 1BZMPBEʹ6TFSΛ௥Ճͯ͠ɺϨεϙϯεͷΫΤϦʔͰGBWPSJUF1SPEVDUTΛऔಘ͢Δ type User implements

    Node { id: ID! favoriteProducts: [Product!]! } mutation AddFavoriteProduct($productID: ID!) { addFavoriteProduct(input: { productID: $productID }) { product { id name isFavorite } user { id # Ωϟογϡߋ৽ favoriteProducts { id name } } } } type AddFavoriteProductPayload { product: Product! user: User! // ௥Ճ }
  57. 57 .VUBJUPOͷεΩʔϚઃܭ w 1BZMPBEͰGBWPSJUF1SPEVDUTΫΤϦʔΛऔಘ͍ͯ͠ͳ͍ͨΊΩϟογϡߋ৽͕͞Εͳ͍໰ ୊ͷղܾࡦ w NVUBUJPO࣮ߦޙʹGBWPSJUF1SPEVDUTΛSFGFUDI͢Δɻ w "QPMMP$MJFOUʹ͸͜ͷͨΊͷػೳ͕ଘࡏ͍ͯ͠·͢ɻ type

    User implements Node { id: ID! favoriteProducts: [Product!]! } query FavoriteProducts { viewer { favoriteProducts { id name } } }
  58. 58 .VUBJUPOͷεΩʔϚઃܭ w 1BZMPBEͰGBWPSJUF1SPEVDUTΫΤϦʔΛऔಘ͍ͯ͠ͳ͍ͨΊΩϟογϡߋ৽͕͞Εͳ͍໰ ୊ͷղܾࡦ w ΩϟογϡΛखಈͰߋ৽͢Δ w (SBQI2-Ωϟογϡ͸ΫϥΠΞϯταΠυͰ࣮૷͢Δ͜ͱͰɺ޷͖ʹ஋Λߋ৽͢ Δ͜ͱ͕Ͱ͖·͢ɻ

    w ͔͠͠ݸਓతʹ͸͋·Γ͓קΊ͠·ͤΜɻ w ϝϯςφϯε͕௿Լ͢ΔͷͰɺಛผͳύϑΥʔϚϯε͕ඞཁͰͳ͍ݶΓ͸͔ͷ ղܾࡦΛ͓͢͢Ί͠·͢ɻ
  59. 59 .VUBJUPOͷεΩʔϚઃܭ w ·ͱΊ w NVUBUJPOʹ͸ɺJOQVUͱฦΓ஋ͷͨΊͷ1BZMPBEܕΛఆٛ͢Δɻ w ΫϥΠΞϯτΩϟογϡߋ৽ͷͨΊʹɺNVUBUJPOͰߋ৽ͨ͠஋Λ1BZMPBEͰฦ͢ɻ w ߋ৽ͷ৔߹͸ɺߋ৽ͨ͠ΦϒδΣΫτΛJEͱͱ΋ʹฦ͢ɻ

    w ৽ن࡞੒ɺ࡟আͷ৔߹͸ w ߋ৽ର৅ͷΦϒδΣΫτΛࢀর͍ͯ͠Δ͢΂ͯͷ਌ͷΦϒδΣΫτΛฦ͢ w εΩʔϚͰ͸ରԠͤͣɺΫϥΠΞϯτ͕3FGFUDI͢Δ w εΩʔϚͰ͸ରԠͤͣɺΫϥΠΞϯτ͕Ωϟογϡߋ৽Λ͢Δ
  60. 4DBMBSͷ׆༻

  61. 61 4DBMBS w 4DBMBS w *E TUSJOH  fl PBU

    *OU w $VTUPN4DBMBS w %BUF5JNF w 63- w ϑϨʔϜϫʔΫʹΑΔ͕(SBQI2-ͷܕͱݴޠͷܕΛόΠϯσΟϯάͰ͖Δ w ྫ͑͹HRMHFOͰ%BUF5JNFܕΛ࡞੒ͯ͠ɺUJNF5JNFܕΛόΠϯσΟϯά͢Δͱɺ (SBQI2-ͱͯ͠%BUF5JNFܕΛड͚औΔ৔߹͸ɺUJNF5JNFܕΛฦͤ͹͍͍ w +TPOͱͷରԠ͸ݸผʹ࣮૷Մೳ w ΫϥΠΞϯταΠυͷBQPMMPJPT΍BQPMMPBOESPJEͰ΋ಉ༷ w 8FCͷ"QPMMPΫϥΠΞϯτʹ͸ͳ͍ʁ
  62. EJSFDUJWFͷ׆༻

  63. 63 EJSFDUJWFͷ׆༻ w ඪ४࢓༷ͷEJSFDUJWF w EFQSFDBUFE w %FQSFDBUFEʹͳͬͨϑΟʔϧυʹࢦఆ͢Δɻ w ΫϥΠΞϯταΠυ͸ɺ2VFSZ͔Βίʔυੜ੒͢Δͱ͖ʹܯࠂͯ͘͠ΕΔ΋ͷ΋͋

    Γ·͢ɻ w ܯࠂ͠ͳ͍৔߹͸IUUQTHJUIVCDPNHRMHPEFQSFDBUFERVFSZΛར༻͢Δͱݕग़ Ͱ͖·͢ɻ w TQFDJ fi FE#Z w $VTUPN4DBMBSͷ࢓༷Λදݱ͢Δɻ
  64. 64 EJSFDUJWFͷ׆༻ w $VTUPNEJSFDUJWF w "QQJGZͰ׆༻͍ͯ͠ΔEJSFDUJWF w JEEJSFDUJWF w ,JOEʹࢦఆͨ͠*%Ҏ֎Λ౉͢ͱΤϥʔʹͳΔ

    w WBMJEBUJPOEJSFDUJWF w 1BUUFSOʹࢦఆͨ͠৚݅ʹҰக͠ͳ͍஋Λ౉͢ͱΤϥʔ ύλʔϯϧʔϧ͸ɺIUUQTHJUIVCDPNHPQMBZHSPVOEWBMJEBUPSͷ΋ͷΛར༻ɻ input AddFavoriteProductInput { productID: ID! @id(kind: "Product") } input CreateAdminInput { name: String! @validate(pattern: “required,min=1,max=100”) }
  65. 65 EJSFDUJWFͷ׆༻ w "QQJGZͰ׆༻͍ͯ͠ΔEJSFDUJWF w BENJO"QQMJDBUJPO.FNCFSEJSFDUJWF w ࢦఆͨ͠ϑΟʔϧυͷ"QQMJDBUJPOʹॴଐ͍ͯ͠ͳ͍"ENJO͕࣮ߦ͢ΔͱΤϥʔ w ੬ऑੑΛ๷͙

    w Ԡ༻ྫͱͯ͠ɺJOQVUͷBQQMJDBUJPO*%Λࢦఆ͍ͯ͠Δ৔߹ʹ !BENJO"QQMJDBUJPO.FNCFSEJSFDUJFWFΛࢦఆ͍ͯ͠ͳ͍৔߹ʹ੩తղੳͰΤϥʔʹ ͢Δ͜ͱ΋Մೳɻ w ͜ΕʹݶΒͣEJSFDUJWFͷઃఆ๨Ε͸ɺ͢΂ͯ੩తղੳՄೳ w ͦͷଞʹ΋಺෦υϝΠϯʹґଘͨ͠EJSFDUJWFΛ͍͔ͭ͘༻ҙ͍ͯ͠ΔͷͰɺڵຯ͕͋ Δํ͸௚઀ฉ͍͍ͯͩ͘͞ɻ input JoinApplicationInput { applicationID: ID! @id(kind: "Application") @adminApplicationMember adminID: ID! @id(kind: "Admin") }
  66. 66 EJSFDUJWFͷ׆༻ w ͓͢͢Ίͷ7BMJEBUJPOEJSFDUJWF w )5.-ͷ$POTUSBJOU7BMJEBUJPOจ๏ʹ४ڌͨ͠EJSFDUJWFΛఆٛ w IUUQTEFWFMPQFSNP[JMMBPSHFO64EPDT8FC(VJEF)5.- $POTUSBJOU@WBMJEBUJPO w

    (SBQI2-ͰεΩʔϚϑΝʔετͳ'PSN7BMJEBUJPO w IUUQTRJJUBDPNTFZBJUFNTEFB w HSBQIRMDPEFHFOUZQFTDSJQUWBMJEBUJPOTDIFNBͰɺϑϩϯτΤϯυͷWBMJEBUJPOΛ ࣗಈੜ੒ w HSBQIRMDPEFHFO͸WBMJEBUJPO༻ͷTDIFNB΋ࣗಈͰੜ੒Ͱ͖Δ ZVQͱ[PE  w IUUQT[FOOEFWDPEFIFYBSUJDMFTGFCBBD
  67. 67 EJSFDUJWFͷ׆༻ w EJSFDUJWFͷ׆༻·ͱΊ w (SBQI2-։ൃʹ͓͍ͯΫϥΠΞϯταΠυͷ%9͕஫໨͞Ε·͕͢ɺόοΫΤϯυͷ %9ʹ͸EJSFDUJWF͕େ͖ͳ໾ׂΛՌ͍ͨͯ͠·͢ɻ w (SBQI2-Λ࢝ΊΔͨΊʹ͸EJSFDUJWF͸ඞਢͰ͸ͳ͍ͨΊݟಀ͞Εͯ͠·͏͜ͱ͕͋Γ ·͕͢ɺ͜ͷΑ͏ʹEJSFDUJWF͸%9Λେ෯ʹվળ͢Δ΋ͷͳͷͰੋඇݕ౼ͯ͠Έͯͩ͘

    ͍͞ɻ
  68. -JOUFSͷ׆༻

  69. 69 -JOUFSͷ׆༻ w εΩʔϚۦಈ։ൃͷϝϦοτͱͯ͠ɺεΩʔϚʹରͯ͠੩తղੳπʔϧΛ࣮૷͢Δ͜ͱ ͕Ͱ͖Δ w ࣾ಺ͷಠࣗͷϧʔϧΛ-JOUFSͱ࣮ͯ͠૷͢Δ͜ͱ΋Մೳ w ྫ͑͹QFSNJTTJPOEJSFDUJWFͷ͚ͭ๨ΕͳͲΛݕग़͢Δ͜ͱ΋Մೳ

  70. 70 -JOUFSͷ׆༻ w FTMJOU w IUUQTHJUIVCDPNBQPMMPHSBQIRMFTMJOUQMVHJOHSBQIRM

  71. 71 -JOUFSͷ׆༻ w "QQJGZ੡ͷ-JOUFS w HRMHPMBDLJE w IUUQTHJUIVCDPNHRMHPMBDLJE w JEͷࢦఆΛ๨Ε͍ͯΔ2VFSZΛݕग़

    w HRMHPOPEFDIFDL w IUUQTHJUIVCDPNHRMHPOPEFDIFDL w /PEFΛ࣮૷͍ͯ͠ͳ͍UZQFΛݕग़ w HRMHPEFQSFDBUFERVFSZ w IUUQTHJUIVCDPNHRMHPEFQSFDBUFERVFSZ w EFQSFDBUFEͳRVFSZΛݕग़ w HRMHPPQUJPOBMTDIFNB w IUUQTHJUIVCDPNHRMHPPQUJPOBMTDIFNB w PQUJPOBMͳ fi FMEΛ4DIFNB͔Βݕग़ w HRMHPRVFSZTUSJOH w IUUQTHJUIVCDPNHRMHPRVFSZTUSJOH w ιʔείʔυͷத͔Β(SBQI2-2VFSZΛநग़
  72. 72 ίʔυੜ੒πʔϧ w "QQJGZ੡ͷίʔυੜ੒ w :BNBTIPVHRMHFOD w IUUQTHJUIVCDPN:BNBTIPVHRMHFOD w (SBQI2-ΫϥΠΞϯτίʔυͷੜ੒

    w "QQJGZͰ͸"1*ςετΛ͢ΔͨΊʹ(Pͷ(SBQI2-ΫϥΠΞϯτΛੜ੒͍ͯ͠·͢ɻ
  73. 73 ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ 4QFBLFS%FDLͷ֓ཁཝʹؔ࿈εϥΠυΛܝࡌ͍ͯ͠·͢