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

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ͦͳଠ

    View Slide

  2. Appify Technologies
    CTO
    ͦͳଠ
    @sonatard

    View Slide

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

    View Slide

  4. ͸͡Ίʹ

    View Slide

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

    View Slide

  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-ͷػೳͷ໨తΛ͔Βཧղ͢Δ͜ͱ͕େ੾Ͱ͢ɻ

    View Slide

  7. 7
    (SBIQ2-ͱ͸
    w جຊ
    w 0WFSGFUDIͷղফ
    w ඞཁͳ஋͚ͩΛΫΤϦʔʹهड़ͯ͠औಘ͢ΔͷͰɺ༨ܭͳϑΟϧʔυͷ஋Λऔಘ͠
    ͳ͍ͰࡁΉ
    w 6OEFSGFUDIͷղফ
    w (SBQIߏ଄ʹͳ͍ͬͯΔͨΊɺϦϨʔγϣϯ͕͋Δ஋͸̍౓ʹऔಘ͢Δ͜ͱ͕Ͱ͖
    Δ
    w εΩʔϚۦಈ։ൃ
    w εΩʔϚΛݩʹόοΫΤϯυͷίʔυͷੜ੒΍ΫϥΠΞϯταΠυͷΫΤϦʔͷ
    هड़Λߦ͏

    View Slide

  8. εΩʔϚۦಈ։ൃ

    View Slide

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

    View Slide

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

    type Query {
    node(id: ID!): Node!
    viewer: User!
    adminViewer: Admin!
    }

    View Slide

  11. 11
    εΩʔϚۦಈ։ൃ
    w (SBQI2-4DIFNBͷఆٛʹ͠ै͍
    ੩తղੳ͕Մೳ
    w -JOUFSͳͲΛࣗ༝ʹ࡞Δ͜ͱ͕
    Ͱ͖Δ
    w (SBQI2-͸DVTUPNEJSFDUJWFͳͲ
    ͰಠࣗͷϧʔϧΛ෇༩͢Δ͜ͱ͕
    Մೳ
    w ಠࣗϧʔϧʹରͯ͠ͷ੩తղੳ
    πʔϧ΋࡞Δ͜ͱ͕Ͱ͖Δ
    w EJSFDUJWFͷষͰޙड़͠·͢

    View Slide

  12. 12
    (SBQI2-εΩʔϚʹΑΔίʔυੜ੒ͷར఺
    w େ෯ʹ࣮૷ྔͷ࡟ݮ͕Մೳ
    6*
    6TFDBTF
    3FQPTJUPSZ
    6*
    (SBQI2-͔Β͸
    7JFX.PEFM͕ฦͬͯ͘ΔͷͰͳ͠
    ੜ੒͞Εͨ(SBQI2-ΫϥΠΞϯτ
    ϨΠϠʔ͸6*͚ͩʹͳΓɺ͋ͱ͸
    ੜ੒͞Εͨ(SBQI2-ΫϥΠΞϯτ
    Λݺͼग़͚ͩ͢ʹͳΔ
    ैདྷͷΞʔΩςΫνϟ (SBQI2-ར༻࣌ͷΞʔΩςΫνϟ

    View Slide

  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-ΫΤϦʔ

    View Slide

  14. 14
    (SBQI2-εΩʔϚʹΑΔίʔυੜ੒ͷར఺
    w ࣮࣭తʹϓϥοτϑΥʔϜ͝ͱͷڞ௨ԽʹΑΔ࣮૷ͷ࡟ݮͱಉ౳ͷޮՌΛಘΔ
    w ,..ͳͲͰͷڞ௨Խͱಉ౳ͷޮՌ
    "OESPJE
    6*
    6TFDBTF 3FQPTJUPSZ
    J046* σʔλΞΫηεΛڞ௨Խ
    ͯ͠ɺ࣮૷Λ࡟ݮ
    ,..
    "OESPJE
    6*
    J046*
    (SBQI2-
    ΫϥΠΞϯτ
    (SBQI2-
    ΫϥΠΞϯτ
    ίʔυੜ੒ʹΑΓ
    ࣮૷Λ࡟ݮ
    ,..ʹΑΔڞ௨Խ͸,PUMJOͰͷ࣮૷͕ඞཁʹͳΔ͕ɺ
    ࣗಈੜ੒Ͱ͸౓΋࣮૷͕ෆཁͳͷͰɺΑΓޮՌ͕ظ଴Ͱ͖Δ

    View Slide

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

    View Slide

  16. (SBQI2-εΩʔϚʹΑΔίʔυੜ੒ͷར఺
    w ΫϥΠΞϯταΠυͰ΋ಉ༷ʹಠཱੑͷ֬อ͸ॏཁͰ͢ɻ
    w ҟͳΔϓϥοτϑΥʔϜͰॻ͍͍ͯΔݶΓɺ଍ฒΈΛἧ͑ͣϓϥοτϑΥʔϜ͝ͱʹ࠷
    దͳॻ͖ํΛͨ͠ํ͕ݸผͰ͸࠷େ଎౓͕ग़·͢ɻ
    w (SBQI2-͸ΫϥΠΞϯτ͝ͱʹ2VFSZΛॻ͘͜ͱ͕Ͱ͖Δͷ͕ϝϦοτͰ͢ɻ
    w ڞ௨Խͯ͠͠·͏ͱ͜ͷϝϦοτΛࣦ͍·͢ɻ
    w 2VFSZͷ౷Ұ͸7JFXʹ΋Өڹ͢ΔͨΊɺ7JFX΋଍ฒΈΛἧ͑Δඞཁ͕Ͱ͖ͯ·͢ɻ
    w 3&45͸͜ͷΑ͏ͳϝϦοτ͕ݩʑ͋ΔΘ͚Ͱ͸ͳ͍ͷͰɺڞ௨ԽʹΑΔଛࣦ͸গͳ͍ɻ
    w "QQJGZͰ͸ɺJ04ͱ"OESPJEͷ6*΍(SBQI2-ΫΤϦʔͷ࣮૷͸ಠཱͯ͠ߦͳ͍ͬͯ·͢ɻ
    16

    View Slide

  17. 17
    (SBQI2-εΩʔϚʹΑΔίʔυੜ੒ͷར఺
    w ݸผʹ࣮૷͢ΔσϝϦοτ͸ɺ࢓༷ͷ౷Ұ͕೉͘͠ͳΔ͜ͱͰ͢ɻ
    w ͲͪΒͷԸܙ΋ड͚͍ͨͱ͖ʹ͸ɺ'MVUUFS΍3FBDU/BUJWFͰ͢΂ͯΛڞ௨Խ͢Δબ୒ࢶ
    ΋͋Γ·͢ɻ
    w ͔͠͠΋ͪΖΜ'MVUUFS΍3FBDU/BUJWFʹ΋՝୊͸͋ΔͷͰɺਖ਼ղ͸͋Γ·ͤΜɻ
    w ΞʔΩςΫτͷ࣮ྗͷݟͤॴ💪

    View Slide

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

    View Slide

  19. 2VFSZͷεΩʔϚઃܭͱೝূೝՄ

    View Slide

  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
    }
    }

    View Slide

  21. 21
    2VFSZͷεΩʔϚઃܭ
    w 3&45ͷΑ͏ͳεΩʔϚ*%ฦͯ͠ɺͦͷ*%Ͱ࣍ͷ2VFSZΛ࣮ߦ
    ΫϥΠΞϯτ αʔό
    (FU0SEFS*%
    PSEFS*%
    (FU1SPEVDU*%#Z0SEFS
    QSPEVDU*%
    (FU1SPEVDU
    QSPEVDUOBNF
    ճͷϥ΢ϯυτϦοϓ͕
    ͋Γɺ(SBQI2-ͷϝϦο
    τΛੜ͔͍ͤͯͳ͍

    View Slide

  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
    αʔό
    ̍ճͷϥ΢ϯυτϦοϓͰࡁΉ

    View Slide

  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ͷ*%Λฦ͍ͯͨ͠Βٙ͏
    *%Λฦ͢ͷͰ͸ͳ͘
    ΦϒδΣΫτΛฦ͢

    View Slide

  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 ೝূͷ࣮૷͸লུ͠·͢

    View Slide

  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
    }

    View Slide

  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ΛࢀরͰ͖͍͍ͯ͸͕ͣɺΤϥʔ
    ʹͳΔɻ

    View Slide

  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͕ฦ͢͜ͱ͕Ͱ͖Δ

    View Slide

  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
    }

    View Slide

  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!
    }

    View Slide

  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)
    }

    View Slide

  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])
    }

    View Slide

  32. 32
    2VFSZͷεΩʔϚઃܭ
    w 3PMFͷ஫ҙ఺
    w 3PMF͸ηΩϡϦςΟରࡦʹ͸ͳΒͳ͍৔߹͕͋Δ
    w 3PMFΛ͚͍ͭͯΔ͔Βͱ͍ͬͯର৅ϑΟʔϧυΛऔಘ͢Δͷʹೝূ৘ใΛ࢖͍ͬͯΔͱ͸
    ݶΒͳ͍
    w ͦͷ৔߹͸ɺಉҰϩʔϧͷҟͳΔϢʔβ͸ΞΫηεͰ͖ͯ͠·͏
    type Product implements Node {
    id: ID!
    name: String!
    secretID: ID! @role(roles: [Admin])
    }

    View Slide

  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])
    }

    View Slide

  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!
    }

    View Slide

  35. 35
    ೝՄ
    w εΩʔϚۦಈ։ൃʹΑΓQFSNJTTJPOEJSFDUJWFΛεΩʔϚͰදݱ͢Δ͜ͱͰɺΫϥΠΞϯταΠ
    υ͸ೝূ৘ใΛεΩʔϚͰ֬ೝ͢Δ͜ͱ͕Ͱ͖Δɻ
    w ผ్υΩϡϝϯτͷ࡞੒΍࣮૷ͷத਎Λ֬ೝ͢Δඞཁ͕ͳ͍ɻ
    w ·ͨೝূͷ࣮૷๨Ε͸஫ҙ͠ͳ͍ͱൃੜͯ͠͠·͏͕ɺ(SBQI2-εΩʔϚΛϨϏϡʔ͢Δ͜ͱͰ
    อূͰ͖Δɻ
    w ݸผͷ࣮૷ΛϨϏϡʔ͢ΔΑΓ΋҆શੑ͕ߴ͘ɺઃఆՕॴ΋໌֬Ͱ͋Δɻ
    w ·ͨ(SBQI2-ͷεΩʔϚΛ੩తղੳ͢Δ͜ͱͰɺQFSNJTTJPOEJSFDUJWFͷ͚ͭ๨ΕͳͲΛݕग़͢
    Δ͜ͱ΋ՄೳʹͳΔɻ
    w SFTPMWFSͷ(SBQIߏ଄ʹैͬͨ஋ͷऔಘͱQFSNJTTJPOEJSFDUJWFʹΑΔೝՄͷ࢓૊Έ͕෼཭͢Δ͜
    ͱͰɺόοΫΤϯυίʔυͷ։ൃମݧ΋େ෯ʹ޲্͠·͢ɻ
    w όοΫΤϯυϩδοΫ͸஋ͷऔಘʹूத͢Ε͹ྑ͍ͨΊɻ

    View Slide

  36. 36
    ೝՄ
    w SPMFEJSFDUJWF΍QFSNJTTJPOEJSFDUJWFͷΑ͏ͳ΋ͷ͸ϑϨʔϜϫʔΫʹΑͬͯ͸༧Ί༻ҙ͞Ε͍ͯ
    Δ৔߹΋͋ΔͷͰௐ΂ͯΈ͍ͯͩ͘͞ɻBVUIEJSFDUJWFͳͲʁ
    w ·ͣ͸ͦΕΛ࢖ͬͯΈͯɺෆ଍͍ͯ͠Δ৔߹ʹ͸ಠࣗʹ࣮૷Λݕ౼ͯ͠Έ͍ͯͩ͘͞ɻ

    View Slide

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

    View Slide

  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!
    }

    View Slide

  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

    View Slide

  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
    }
    }
    }

    View Slide

  41. 41
    2VFSͷεΩʔϚઃܭ
    w VOJPOͱJOUFSGBDF
    w ͕࣌ؒͳ͍ͨΊޙ೔ࢿྉ௥Ճ͠·͢

    View Slide

  42. 42
    2VFSͷεΩʔϚઃܭ
    w Χʔιϧϕʔεͷϖʔδωʔγϣϯ࢓༷
    w 3FMBZDVSTPSDPOOFDUJPOTQFDJ
    fi
    DBUJPO
    w ༷ʑͳͱ͜ΖͰઆ໌͞Ε͍ͯΔͱࢥ͏ͷͰجຊతͳ࿩͸লུ͠·͢ɻ
    w ࢓༷ॻʹΑΔͱ3FMBZʹ͸͍͔ͭ͘ͷ࢓༷͕ଘࡏ͢Δɻ
    w 3FMBZ$MBTTJD
    w 3FMBZ-FHBDZ
    w 3FMBZ.PEFSO
    w ͔ͦ͠͠ΕͧΕͷઆ໌͕࢓༷ʹॻ͔Ε͍ͯͳ͍ͨΊৄࡉ͸ෆ໌

    View Slide

  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!
    }

    View Slide

  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

    View Slide

  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!
    }

    View Slide

  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!
    }

    View Slide

  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!
    }

    View Slide

  48. 48
    2VFSZͷεΩʔϚઃܭͱೝূೝՄ
    w Χʔιϧϕʔεͷϖʔδωʔγϣϯ·ͱΊ
    w ݱࡏ͸ɺ3FMBZ.PEFSOʹै͏͜ͱ͕҆શ
    w ͔͠͠ݱ࣮తʹ͸3FMBZ.PEFSOͷ&EHFTΛ࢖͏͜ͱΑΓ΋OPEFTͷํ͕࣮૷͕γϯϓϧʹͳΔ
    ͷͰɺͪ͜ΒΛ࠾༻͢Δ͜ͱ΋Մೳ
    w ඇඪ४ͳͷͰҰఆͷϦεΫΛड͚ೖΕΔඞཁ͕͋Δ

    View Slide

  49. 49
    2VFSZͷεΩʔϚઃܭͱೝূೝՄ
    w εΩʔϚઃܭ·ͱΊ
    w Ҏ্ͷج൫͕੔͍ɺཧղ͕Ͱ͖Δͱ(SBQI2-ͷόοΫΤϯυ։ൃ͸γϯϓϧʹͳΓ·͢ɻ
    w (SBQIߏ଄ͰΦϒδΣΫτΛฦ͢
    w 3&45Ͱ*%Λฦ͍ͯͨ͠ͱ͜ΖΛɺΦϒδΣΫτʹ͢Δ
    w ΦϒδΣΫτؒͷϦϨʔγϣϯΛઃܭ͢Δ͜ͱ͸3&45ͱมΘΒͳ͍
    w ஋औಘͷ࣮૷ޙʹSPMFɺQFSNJTTJPOEJSFDUJWFΛઃఆ͢Δ
    w 3&45ͱ͸ҟͳΓ஋औಘͱೝՄΛ෼཭ͯ͠ɺ஋औಘʹϑΥʔΧεͨ͠γϯϓϧͳ࣮૷͕Մೳ
    w OPEFɺ$POOFDUJPOʹै͏
    w Ұൠతͳ࢓༷͕ఆٛ͞Ε͍ͯΔ

    View Slide

  50. .VUBJUPOͷεΩʔϚઃܭ

    View Slide

  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ͳͲ

    View Slide

  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!
    }

    View Slide

  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!
    }

    View Slide

  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


    }
    }
    }

    View Slide

  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


    }
    }
    }

    View Slide

  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! // ௥Ճ
    }

    View Slide

  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
    }
    }
    }

    View Slide

  58. 58
    .VUBJUPOͷεΩʔϚઃܭ
    w 1BZMPBEͰGBWPSJUF1SPEVDUTΫΤϦʔΛऔಘ͍ͯ͠ͳ͍ͨΊΩϟογϡߋ৽͕͞Εͳ͍໰
    ୊ͷղܾࡦ
    w ΩϟογϡΛखಈͰߋ৽͢Δ
    w (SBQI2-Ωϟογϡ͸ΫϥΠΞϯταΠυͰ࣮૷͢Δ͜ͱͰɺ޷͖ʹ஋Λߋ৽͢
    Δ͜ͱ͕Ͱ͖·͢ɻ
    w ͔͠͠ݸਓతʹ͸͋·Γ͓קΊ͠·ͤΜɻ
    w ϝϯςφϯε͕௿Լ͢ΔͷͰɺಛผͳύϑΥʔϚϯε͕ඞཁͰͳ͍ݶΓ͸͔ͷ
    ղܾࡦΛ͓͢͢Ί͠·͢ɻ

    View Slide

  59. 59
    .VUBJUPOͷεΩʔϚઃܭ
    w ·ͱΊ
    w NVUBUJPOʹ͸ɺJOQVUͱฦΓ஋ͷͨΊͷ1BZMPBEܕΛఆٛ͢Δɻ
    w ΫϥΠΞϯτΩϟογϡߋ৽ͷͨΊʹɺNVUBUJPOͰߋ৽ͨ͠஋Λ1BZMPBEͰฦ͢ɻ
    w ߋ৽ͷ৔߹͸ɺߋ৽ͨ͠ΦϒδΣΫτΛJEͱͱ΋ʹฦ͢ɻ
    w ৽ن࡞੒ɺ࡟আͷ৔߹͸
    w ߋ৽ର৅ͷΦϒδΣΫτΛࢀর͍ͯ͠Δ͢΂ͯͷ਌ͷΦϒδΣΫτΛฦ͢
    w εΩʔϚͰ͸ରԠͤͣɺΫϥΠΞϯτ͕3FGFUDI͢Δ
    w εΩʔϚͰ͸ରԠͤͣɺΫϥΠΞϯτ͕Ωϟογϡߋ৽Λ͢Δ

    View Slide

  60. 4DBMBSͷ׆༻

    View Slide

  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ΫϥΠΞϯτʹ͸ͳ͍ʁ

    View Slide

  62. EJSFDUJWFͷ׆༻

    View Slide

  63. 63
    EJSFDUJWFͷ׆༻
    w ඪ४࢓༷ͷEJSFDUJWF
    w EFQSFDBUFE
    w %FQSFDBUFEʹͳͬͨϑΟʔϧυʹࢦఆ͢Δɻ
    w ΫϥΠΞϯταΠυ͸ɺ2VFSZ͔Βίʔυੜ੒͢Δͱ͖ʹܯࠂͯ͘͠ΕΔ΋ͷ΋͋
    Γ·͢ɻ
    w ܯࠂ͠ͳ͍৔߹͸IUUQTHJUIVCDPNHRMHPEFQSFDBUFERVFSZΛར༻͢Δͱݕग़
    Ͱ͖·͢ɻ
    w TQFDJ
    fi
    FE#Z
    w $VTUPN4DBMBSͷ࢓༷Λදݱ͢Δɻ

    View Slide

  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”)
    }

    View Slide

  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")
    }

    View Slide

  66. 66
    EJSFDUJWFͷ׆༻
    w ͓͢͢Ίͷ7BMJEBUJPOEJSFDUJWF
    w )5.-ͷ$POTUSBJOU7BMJEBUJPOจ๏ʹ४ڌͨ͠EJSFDUJWFΛఆٛ
    w IUUQTEFWFMPQFSNP[JMMBPSHFO64EPDT8FC(VJEF)5.-
    [email protected]
    w (SBQI2-ͰεΩʔϚϑΝʔετͳ'PSN7BMJEBUJPO
    w IUUQTRJJUBDPNTFZBJUFNTEFB
    w HSBQIRMDPEFHFOUZQFTDSJQUWBMJEBUJPOTDIFNBͰɺϑϩϯτΤϯυͷWBMJEBUJPOΛ
    ࣗಈੜ੒
    w HSBQIRMDPEFHFO͸WBMJEBUJPO༻ͷTDIFNB΋ࣗಈͰੜ੒Ͱ͖Δ ZVQͱ[PE

    w IUUQT[FOOEFWDPEFIFYBSUJDMFTGFCBBD

    View Slide

  67. 67
    EJSFDUJWFͷ׆༻
    w EJSFDUJWFͷ׆༻·ͱΊ
    w (SBQI2-։ൃʹ͓͍ͯΫϥΠΞϯταΠυͷ%9͕஫໨͞Ε·͕͢ɺόοΫΤϯυͷ
    %9ʹ͸EJSFDUJWF͕େ͖ͳ໾ׂΛՌ͍ͨͯ͠·͢ɻ
    w (SBQI2-Λ࢝ΊΔͨΊʹ͸EJSFDUJWF͸ඞਢͰ͸ͳ͍ͨΊݟಀ͞Εͯ͠·͏͜ͱ͕͋Γ
    ·͕͢ɺ͜ͷΑ͏ʹEJSFDUJWF͸%9Λେ෯ʹվળ͢Δ΋ͷͳͷͰੋඇݕ౼ͯ͠Έͯͩ͘
    ͍͞ɻ

    View Slide

  68. -JOUFSͷ׆༻

    View Slide

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

    View Slide

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

    View Slide

  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Λநग़

    View Slide

  72. 72
    ίʔυੜ੒πʔϧ
    w "QQJGZ੡ͷίʔυੜ੒
    w :BNBTIPVHRMHFOD
    w IUUQTHJUIVCDPN:BNBTIPVHRMHFOD
    w (SBQI2-ΫϥΠΞϯτίʔυͷੜ੒
    w "QQJGZͰ͸"1*ςετΛ͢ΔͨΊʹ(Pͷ(SBQI2-ΫϥΠΞϯτΛੜ੒͍ͯ͠·͢ɻ

    View Slide

  73. 73
    ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠
    4QFBLFS%FDLͷ֓ཁཝʹؔ࿈εϥΠυΛܝࡌ͍ͯ͠·͢

    View Slide