Slide 1

Slide 1 text

࣮ફ(SBQI2-PO4DBMB 4DBMBؔ੢4VNNJU !QFUJUWJPMFU

Slide 2

Slide 2 text

͸͡Ίʹ w αϯϓϧίʔυ w IUUQTHJUIVCDPNQFUJUWJPMFUHSBQIRM@PO@TDBMB w ϦϯΫू w IUUQTHSBQIRMHJUIVCJPMFBSO w IUUQTXXXIPXUPHSBQIRMDPN w IUUQTTBOHSJBHSBQIRMPSHMFBSO 2

Slide 3

Slide 3 text

Ͱɺ୭ʁ w খࢵ߂و w !QFUJUWJPMFU w 'SJOHFגࣜձࣾ w ؔ੢ग़਎ w ڈ೥ͷ4DBMBؔ੢Ͱ͸ϝλϓϩάϥϛϯάʹ͍ͭͯ 3

Slide 4

Slide 4 text

(SBQI2- w ੝Γ্͕ͬͯΔ͚ͲΑ͘஌Βͳ͍ w ஌ͬͯΔ͚Ͳ4DBMBͰग़དྷΔͷʁ w ΍ͬͯΔ͚Ͳ΍Γํʹࣗ৴ͳ͍ 4

Slide 5

Slide 5 text

ࠓ೔ͷΰʔϧ ʮ࣍ͷϓϩδΣΫτͰ͸ ɹ(SBQI2-Λ 4DBMBͰ ΍Γ͍ͨʂʯ 5

Slide 6

Slide 6 text

ࠓ೔࿩͢͜ͱ࿩͞ͳ͍͜ͱ ࿩͢͜ͱ w (SBQI2-ʹ͍ͭͯ͟ʔͬͱ w 4DBMBͰͷ࣮ફʹ͍ͭͯ w ͳͥ(SBQI2-ͳͷ͔ɺͲ͏΍͍ͬͯΔͷ͔ w جຊతͳ࣮૷ͷ࢓ํ͔Βઃܭʹ͍ͭͯ ࿩͞ͳ͍͜ͱ w 4DBMBҎ֎Ͱͷ(SBQI2-ʹ͍ͭͯ w 4VCTDSJQUJPO! w ͦͷଞࡉ͔͍࢓༷ 6

Slide 7

Slide 7 text

ࠓ೔࿩͢͜ͱ w (SBQI2-ͱ͸ w Ͳ͏cͳͥ (SBQI2-Λ࣮ફ͍ͯ͠Δͷ͔ w 4DBMBͰͷ(SBQI2- w 4DBMBͰͷ(SBQI2-࣮ફ 7

Slide 8

Slide 8 text

ɹ(SBQI2-ʁ 8

Slide 9

Slide 9 text

9 (SBQI2-JTBOPQFOTPVSDFEBUBRVFSZBOE NBOJQVMBUJPOMBOHVBHF BOEBSVOUJNFGPSGVMpMMJOH RVFSJFTXJUIFYJTUJOHEBUB https://en.wikipedia.org/wiki/GraphQL

Slide 10

Slide 10 text

(SBQI2-ͱ͸ w 'BDFCPPL͕೥ࠒʹൃදͨ͠΋ͷ w ಺෦తʹ͸೥͘Β͍͔Β։ൃ w 2VFSZ-BOHVBHF w "1*ͷͨΊͷΫΤϦݴޠ w (SBQI2-ͱ͍͏࢓༷͕͋Δ w ެࣜIUUQTHSBQIRMPSH 10

Slide 11

Slide 11 text

(SBQI2-ͱ͸ w ࢓༷͸'BDFCPPL͕ࡦఆ͍ͯ͠Δ w IUUQTGBDFCPPLHJUIVCJPHSBQIRM w IUUQTHJUIVCDPNGBDFCPPLHSBQIRM w ࢓༷ͱ͖ͯͬͪ͠ΓͱܾΊΒΕ͍ͯΔ w (JUIVCͰΦʔϓϯʹٞ࿦͞Ε͍ͯΔ w ֓೦Ͱ͸ͳ͍ 11

Slide 12

Slide 12 text

(SBQI2-ͱ͸ w (JUIVC͕ʹ"1*Wͱͯ͠(SBQI2-Λ࠾༻ w ͔ͦ͜Β஌໊౓্͕͕ͬͨ 12

Slide 13

Slide 13 text

3&45GVMͱൺ΂ͯΈΔ w (SBQI2-੝Γ্͕ͬͯΔ 13

Slide 14

Slide 14 text

(SBQI2-ͱ͸ 14 https://gql.foundation/

Slide 15

Slide 15 text

(SBQI2-ΩςΔ 15

Slide 16

Slide 16 text

(SBQI2-ͱ͸ w ܕγεςϜΛఏڙ͍ͯ͠Δ w "1*ΠϯλϑΣʔεͰ࢖༻͢ΔܕΛఆٛͰ͖Δ w ඞཁͳσʔλΛΫϥΠΞϯτ͔Β໰͍߹ΘͤΔ w ໰͍߹Θͤ ΫΤϦ ༻ͷݴޠ࢓༷ w ඞཁͳσʔλ ϑΟʔϧυ ͚ͩΛࢦఆͰ͖Δ w (SBQIJ2-͕ඇৗʹศར w ϒϥ΢βͰಈ͘*%&తͳ΋ͷ 16

Slide 17

Slide 17 text

(SBQI2-ͷ༷ࢠ (SBQIJ2- 17

Slide 18

Slide 18 text

(SBQI2-ͱ͸ w ؔ࿈͢ΔΦϒδΣΫτ΋·ͱΊͯऔಘՄೳ w ؔ࿈Λܕͱͯ͠දݱ͓͚ͯ͠͹ྑ͍ w υΩϡϝϯτ΋εΩʔϚ͔Βࣗಈੜ੒ 18

Slide 19

Slide 19 text

3&45GVM (SBQI2-ͱͷൺֱ 19 QSPT DPOT 3&45GVM w Α͘஌ΒΕ͍ͯΔ w Ϧιʔεͱ"1*ͷؔ࿈෇͚ w )551ͱ૬ੑ͕ྑ͍ w Ϧιʔεͷઃܭ͕େม w ϦΫΤετճ਺૿͕͑ͪ (SBQI2- w *'Λܕఆٛग़དྷΔ w ϦΫΤετΛ࠷దԽग़དྷΔ w ΫΤϦͷ੹຿ΛΫϥΠΞϯτ ʹدͤΔ͜ͱ͕ग़དྷΔ w ஌ݟࣄྫ͕ཷ·͍ͬͯͳ͍ w )551ͱ૬ੑ͕ྑ͘ͳ͍ w ϑΝΠϧΛѻ͍ਏ͍ FHը૾

Slide 20

Slide 20 text

(SBQI2-ͷ࢖͍Ͳ͜Ζ͸Ͳ͔͜ ओ؍ w ࢖͍΍͍͢ͷ͸಺෦޲͚"1* w ߟ͑Δ͜ͱ͕গͳ͘ɺγϯϓϧʹอͪ΍͍͢ w ֎෦޲͚͸·ͩ3&45ͷํ͕Ұൠత͔΋ w #''ʹ΋ྑͦ͞͏ w ෳ਺ͷόοΫΤϯυΛଋͶͨ"1*Λ࡞Γ΍͍͢ w ඞཁͳσʔλΛඞཁͳ࣌ʹऔಘ w ෆඞཁͳͷʹຖճ໰͍߹ΘͤΔɺΛආ͚Δ 20

Slide 21

Slide 21 text

஫ҙ఺ w (SBQI2-͸ศར͕ͩɺۜͷ஄ؙͰ͸ͳ͍ w Α͘ग़དྷͨ3&45ͳΒɺ(SBQI2-Ͱ࣮ݱ͍ͯ͠Δ ͜ͱ͸શͯग़དྷΔ w ʮ(SBQI2-ʹ͔͠ग़དྷͳ͍͜ͱʯ͸ແ͍ w αϘͬͯ΋͋Δఔ౓࣮ݱͰ͖Δͷ͕(SBQI2- w "1*ͷܕදݱ υΩϡϝϯτࣗಈੜ੒ͳͲ w ౰વࣦΘΕΔ΋ͷ΋͋Δ 21

Slide 22

Slide 22 text

(SBQI2-ͷUZQFTZTUFN ओͳొ৔ਓ෺͸͜ͷลΓ w OVMMBCMFOPOOVMM -JTU w 0CKFDU5ZQF w *OUFSGBDF w 4DBMBS5ZQF w &OVN5ZQF w 6OJPO5ZQF w "SHVNFOU w *OQVU0CKFDU5ZQF 22

Slide 23

Slide 23 text

(SBQI2-ͷUZQFTZTUFN ओͳొ৔ਓ෺͸͜ͷลΓ w OVMMBCMFOPOOVMM -JTU w 0CKFDU5ZQF w *OUFSGBDF w 4DBMBS5ZQF w &OVN5ZQF w 6OJPO5ZQF w "SHVNFOU w *OQVU0CKFDU5ZQF 23

Slide 24

Slide 24 text

OVMMBCMFOPOOVMM -JTU w OVMMBCMFOPOOVMM w AA͕͍ͭͨΒOPOOVMM w 0QUJPO<">A͔A"Aͷҧ͍ w -JTU w <9>Ͱ9ͷ-JTUʹͳΔ w <4USJOH>͸OVMMBCMFͳ4USJOHͷOVMMBCMFͳ-JTU type User { name: String! ←!Ͱnon-null projects: [Project!]! ←[x]ͰxͷList } 24 (SBQI2-ͷܕఆٛ

Slide 25

Slide 25 text

OVMMBCMFOPOOVMM -JTU type User { name: String! projects: [Project!]! } 25 case class User(name: String, projects: Seq[Project]) type User { name: String projects: [Project] } case class User( name: Option[String], projects: Option[Seq[Option[Project]]])

Slide 26

Slide 26 text

࣮ફʁ 26

Slide 27

Slide 27 text

Ͳ͏࣮ફ͍ͯ͠Δͷ͔ w ޿ࠂγεςϜͷ؅ཧը໘޲͚"1* w ޿ࠂ഑৴ͷઃఆ΍ϨϙʔτΛӾཡͰ͖Δ w ಺෦޲͚"1*ͱͯ͠৴པͰ͖ΔΫϥΠΞϯτʹఏڙ w ΫϥΠΞϯτ͸&MNͰॻ͔Ε͍ͯΔ w ,VCFSOFUFTʹσϓϩΠ͍ͯ͠Δ w 2VFSZͱ.VUBUJPOͲͪΒ΋(SBQI2- w ϑΝΠϧͷ\Ξοϓ μ΢ϯ^ϩʔυ͸ඇ(SBQI2- 27

Slide 28

Slide 28 text

ͳͥ(SBQI2-Λ࠾༻ͨ͠ͷ͔ w ؅ཧը໘࡞Δ͚ͲɺͲ͏΍ͬͯ࡞Δʁ w Ϧιʔεઃܭ͸೉͍͠ͷ͸ݟ͑ͯΔ w ʮ͜ͷը໘։͘ͷʹ"1*Λճ΋ୟ͘ͷʁʯ w ʮEPDTͱҧ͏Ϩεϙϯεฦͬͯ͘ΔͰ͚͢Ͳʁʯ w 4XBHHFS+40/4DIFNB࢖͏ʜʁ w %%%ͷ3FQPTJUPSZ͕pOE#Z9YYͩΒ͚ w ը໘͔ΒͷϦΫΤετ͕ଟ༷ 28

Slide 29

Slide 29 text

3&45GVM (SBQI2-ͱͷൺֱ 29 QSPT DPOT 3&45GVM w Α͘஌ΒΕ͍ͯΔ w Ϧιʔεͱ"1*ͷؔ࿈෇͚ w )551ͱ૬ੑ͕ྑ͍ w Ϧιʔεͷઃܭ͕େม w ϦΫΤετճ਺૿͕͑ͪ (SBQI2- w *'Λܕఆٛग़དྷΔ w ϦΫΤετΛ࠷దԽग़དྷΔ w ΫΤϦͷ੹຿ΛΫϥΠΞϯτ ʹدͤΔ͜ͱ͕ग़དྷΔ w ஌ݟࣄྫ͕ཷ·͍ͬͯͳ͍ w )551ͱ૬ੑ͕ྑ͘ͳ͍ w ϑΝΠϧΛѻ͍ਏ͍ FHը૾

Slide 30

Slide 30 text

3&45GVM (SBQI2-ͱͷൺֱ 30 QSPT DPOT 3&45GVM w Α͘஌ΒΕ͍ͯΔ w Ϧιʔεͱ"1*ͷؔ࿈෇͚ w )551ͱ૬ੑ͕ྑ͍ w Ϧιʔεͷઃܭ͕େม w ϦΫΤετճ਺૿͕͑ͪ (SBQI2- w *'Λܕఆٛग़དྷΔ w ϦΫΤετΛ࠷దԽग़དྷΔ w ΫΤϦͷ੹຿ΛΫϥΠΞϯτ ʹدͤΔ͜ͱ͕ग़དྷΔ w ஌ݟࣄྫ͕ཷ·͍ͬͯͳ͍ w )551ͱ૬ੑ͕ྑ͘ͳ͍ w ϑΝΠϧΛѻ͍ਏ͍ FHը૾ ਏ͍ͱࢥ͍ͬͯΔͱ͜ΖΛ େମղܾͰ͖ͦ͏ͳݟࠐΈ

Slide 31

Slide 31 text

3&45GVM (SBQI2-ͱͷൺֱ 31 QSPT DPOT 3&45GVM w Α͘஌ΒΕ͍ͯΔ w Ϧιʔεͱ"1*ͷؔ࿈෇͚ w )551ͱ૬ੑ͕ྑ͍ w Ϧιʔεͷઃܭ͕େม w ϦΫΤετճ਺૿͕͑ͪ (SBQI2- w *'Λܕఆٛग़དྷΔ w ϦΫΤετΛ࠷దԽग़དྷΔ w ΫΤϦͷ੹຿ΛΫϥΠΞϯτ ʹدͤΔ͜ͱ͕ग़དྷΔ w ஌ݟࣄྫ͕ཷ·͍ͬͯͳ͍ w )551ͱ૬ੑ͕ྑ͘ͳ͍ w ϑΝΠϧΛѻ͍ਏ͍ FHը૾ ੾Γ୓͍͍֮ͯ͘ޛͰ Կͱ͔ͳΓͦ͏

Slide 32

Slide 32 text

ͳͥ(SBQI2-Λ࠾༻ͨ͠ͷ͔ w ͋ͱ͸λΠϛϯά w ৽͍͠ϓϩμΫτ։ൃͷػձ͕͋ͬͨ w (SBQI2-ʹϚονͨ͠ཁ݅ͩͬͨ w νʔϜϝϯόʹ΋(SBQI2-΍Γ͍ͨਓ͕͍ͨ w ϑϩϯτΤϯυʹ͍ͨͷ͕޾ӡ 32

Slide 33

Slide 33 text

(SBQI2-Λ࠾༻ͯ͠Ͳ͏ʁ w "1*ઃܭͰ೰Ή͜ͱ͕গͳͯ͘޾ͤ w ΫϥΠΞϯτͷ͜ͱΑ͘஌Βͳͯ͘΋Կͱ͔ͳΔ w ཪػೳΛఏڙग़དྷ͍ͯΔ w (SBQIJ2-͕Ұ࣌తͳ؅ཧը໘ʹ w 4DBMBͰඇৗʹ΍Γ΍͍͢ 33

Slide 34

Slide 34 text

4DBMBͰͷ(SBQI2- 34

Slide 35

Slide 35 text

4DBMBͰͷ(SBQI2- w TBOHSJBHSBQIRMTBOHSJB w (SBQI2-ϥΠϒϥϦ!0MFH*MZFOLP w 4DBMBͰ(SBQI2-΍ΔͳΒίϨ w ಛఆͷ8FC"QQ'SBNFXPSLʹґଘ͠ͳ͍ w *'͕+40/ʹͳ͍ͬͯΔͷͰࣗ༝ʹબ΂Δ w +T7BMVFతͳ΋ͷΛύʔεͯ͠(SBQI2-ΫΤϦ ͱ࣮ͯ͠ߦ͠ɺ+T7BMVFతͳ΋ͷΛฦ͢ 35

Slide 36

Slide 36 text

implicit val userType: ObjectType[Ctx, User] = ObjectType(name = "User", fields = fields[Ctx, User]( Field("id", IDType, resolve = { ctx => ctx.value.id }), Field("name", StringType, resolve = { ctx: Context[Ctx, User] => ctx.value.name }), Field("status", userStatusType, resolve = { ctx => ctx.value.status }), Field("projects", ListType(projectType), resolve = { ctx => ProjectResolver.byUser(ctx.value) }) ) ) 0CKFDU5ZQF type User { id: ID! name: String! status: UserStatus! projects: [Project!]! } 36 (SBQI2-ͷܕఆٛ 4BOHSJBͰͷఆٛ

Slide 37

Slide 37 text

type User { id: ID! name: String! status: UserStatus! projects: [Project!]! } implicit val userType: ObjectType[Ctx, User] = ObjectType(name = "User", fields = fields[Ctx, User]( Field("id", IDType, resolve = { ctx => ctx.value.id }), Field("name", StringType, resolve = { ctx: Context[Ctx, User] => ctx.value.name }), Field("status", userStatusType, resolve = { ctx => ctx.value.status }), Field("projects", ListType(projectType), resolve = { ctx => ProjectResolver.byUser(ctx.value) }) ) ) 0CKFDU5ZQF 37

Slide 38

Slide 38 text

type User { id: ID! name: String! status: UserStatus! projects: [Project!]! } implicit val userType: ObjectType[Ctx, User] = ObjectType(name = "User", fields = fields[Ctx, User]( Field("id", IDType, resolve = { ctx => ctx.value.id }), Field("name", StringType, resolve = { ctx: Context[Ctx, User] => ctx.value.name }), Field("status", userStatusType, resolve = { ctx => ctx.value.status }), Field("projects", ListType(projectType), resolve = { ctx => ProjectResolver.byUser(ctx.value) }) ) ) 0CKFDU5ZQF 38 0CKFDU5ZQFʹ ରԠ෇͚Δܕ

Slide 39

Slide 39 text

type User { id: ID! name: String! status: UserStatus! projects: [Project!]! } implicit val userType: ObjectType[Ctx, User] = ObjectType(name = "User", fields = fields[Ctx, User]( Field("id", IDType, resolve = { ctx => ctx.value.id }), Field("name", StringType, resolve = { ctx: Context[Ctx, User] => ctx.value.name }), Field("status", userStatusType, resolve = { ctx => ctx.value.status }), Field("projects", ListType(projectType), resolve = { ctx => ProjectResolver.byUser(ctx.value) }) ) ) 0CKFDU5ZQF 39 $POUFYU ޙड़

Slide 40

Slide 40 text

type User { id: ID! name: String! status: UserStatus! projects: [Project!]! } implicit val userType: ObjectType[Ctx, User] = ObjectType(name = "User", fields = fields[Ctx, User]( Field("id", IDType, resolve = { ctx => ctx.value.id }), Field("name", StringType, resolve = { ctx: Context[Ctx, User] => ctx.value.name }), Field("status", userStatusType, resolve = { ctx => ctx.value.status }), Field("projects", ListType(projectType), resolve = { ctx => ProjectResolver.byUser(ctx.value) }) ) ) 0CKFDU5ZQF 40 ϑΟʔϧυఆٛ

Slide 41

Slide 41 text

type User { id: ID! name: String! status: UserStatus! projects: [Project!]! } implicit val userType: ObjectType[Ctx, User] = ObjectType(name = "User", fields = fields[Ctx, User]( Field("id", IDType, resolve = { ctx => ctx.value.id }), Field("name", StringType, resolve = { ctx: Context[Ctx, User] => ctx.value.name }), Field("status", userStatusType, resolve = { ctx => ctx.value.status }), Field("projects", ListType(projectType), resolve = { ctx => ProjectResolver.byUser(ctx.value) }) ) ) 0CKFDU5ZQF 41 AOBNFAϑΟʔϧυ

Slide 42

Slide 42 text

type User { id: ID! name: String! status: UserStatus! projects: [Project!]! } implicit val userType: ObjectType[Ctx, User] = ObjectType(name = "User", fields = fields[Ctx, User]( Field("id", IDType, resolve = { ctx => ctx.value.id }), Field("name", StringType, resolve = { ctx: Context[Ctx, User] => ctx.value.name }), Field("status", userStatusType, resolve = { ctx => ctx.value.status }), Field("projects", ListType(projectType), resolve = { ctx => ProjectResolver.byUser(ctx.value) }) ) ) 0CKFDU5ZQF 42 SFTPMWFؔ਺ͷ$POUFYUʹ͸ w $POUFYU $UY w 7BMVF $IBSBDUFS ͕٧·͍ͬͯΔ

Slide 43

Slide 43 text

type User { id: ID! name: String! status: UserStatus! projects: [Project!]! } implicit val userType: ObjectType[Ctx, User] = ObjectType(name = "User", fields = fields[Ctx, User]( Field("id", IDType, resolve = { ctx => ctx.value.id }), Field("name", StringType, resolve = { ctx: Context[Ctx, User] => ctx.value.name }), Field("status", userStatusType, resolve = { ctx => ctx.value.status }), Field("projects", ListType(projectType), resolve = { ctx => ProjectResolver.byUser(ctx.value) }) ) ) 0CKFDU5ZQF 43 $POUFYUWBMVFͰ6TFSܕͷ ΠϯελϯεʹΞΫηεՄೳ

Slide 44

Slide 44 text

implicit val userType: ObjectType[Ctx, User] = ObjectType(name = "User", fields = fields[Ctx, User]( Field("id", IDType, resolve = { ctx => ctx.value.id }), Field("name", StringType, resolve = { ctx: Context[Ctx, User] => ctx.value.name }), Field("status", userStatusType, resolve = { ctx => ctx.value.status }), Field("projects", ListType(projectType), resolve = { ctx => ProjectResolver.byUser(ctx.value) }) ) ) type User { id: ID! name: String! status: UserStatus! projects: [Project!]! } 0CKFDU5ZQF 44 <">Λ-JTU5ZQF "5ZQF Ͱදݱ OPOOVMMͳ1SPKFDUͷ OPOOVMMͳ-JTU

Slide 45

Slide 45 text

implicit val userType: ObjectType[Ctx, User] = ObjectType(name = "User", fields = fields[Ctx, User]( Field("id", IDType, resolve = { ctx => ctx.value.id }), Field("name", StringType, resolve = { ctx: Context[Ctx, User] => ctx.value.name }), Field("status", userStatusType, resolve = { ctx => ctx.value.status }), Field("projects", ListType(projectType), resolve = { ctx => ProjectResolver.byUser(ctx.value) }) ) ) type User { id: ID! name: String! status: UserStatus! projects: [Project!]! } 0CKFDU5ZQF 45 SFTPMWFؔ਺ͷதͰ%#ΞΫηεͳͲͯ͠ ؔ࿈ΦϒδΣΫτΛऔಘग़དྷΔ

Slide 46

Slide 46 text

implicit val userType: ObjectType[Ctx, User] = ObjectType(name = "User", fields = fields[Ctx, User]( Field("id", IDType, resolve = { ctx => ctx.value.id }), Field("name", StringType, resolve = { ctx: Context[Ctx, User] => ctx.value.name }), Field("status", userStatusType, resolve = { ctx => ctx.value.status }), Field("projects", ListType(projectType), resolve = { ctx => ProjectResolver.byUser(ctx.value) }) ) ) 0CKFDU5ZQF 46 6TFSOBNFΛऔಘ͢Δ2VFSZΛॻ͘ͱɺ VTFS5ZQFͷOBNFϑΟʔϧυͷSFTPMWFͷ݁ՌΛಘΔ

Slide 47

Slide 47 text

implicit val userType: ObjectType[Ctx, User] = ObjectType(name = "User", fields = fields[Ctx, User]( Field("id", IDType, resolve = { ctx => ctx.value.id }), Field("name", StringType, resolve = { ctx: Context[Ctx, User] => ctx.value.name }), Field("status", userStatusType, resolve = { ctx => ctx.value.status }), Field("projects", ListType(projectType), resolve = { ctx => ProjectResolver.byUser(ctx.value) }) ) ) 0CKFDU5ZQF 47 %#ΞΫηε͕ඞཁʹͳΔϑΟʔϧυ͕͋ͬͯ΋ ͦͷϑΟʔϧυΛऔಘ͠ͳ͍ͳΒSFTPMWFؔ਺͕࣮ߦ͞ Εͣ%#ΞΫηε΋ൃੜ͠ͳ͍

Slide 48

Slide 48 text

0CKFDU5ZQF w ఆ͕ٛΊΜͲ͍͘͞ʁ 48 implicit val userType: ObjectType[Ctx, User] = ObjectType(name = "User", fields = fields[Ctx, User]( Field("id", IDType, resolve = { ctx => ctx.value.id }), Field("name", StringType, resolve = { ctx: Context[Ctx, User] => ctx.value.name }), Field("status", userStatusType, resolve = { ctx => ctx.value.status }), Field("projects", ListType(projectType), resolve = { ctx => ProjectResolver.byUser(ctx.value) }) ) )

Slide 49

Slide 49 text

0CKFDU5ZQF w ͦ͜ͰϚΫϩ w DBTFDMBTTͳΒ͍͍ײ͡ʹղܾͯ͘͠ΕΔ w ґଘ͢Δଞͷಠࣗܕ͸JNQMJDJUͰએݴ͓ͯ͘͜͠ͱ w ࠓճͳΒQSPKFDU5ZQFͱ͔ import sangria.macros.derive val userType: ObjectType[Ctx, User] = derive.deriveObjectType() 49

Slide 50

Slide 50 text

&OVN5ZQF val userStatusType = EnumType[UserStatus]( "UserStatus", values = List( EnumValue[UserStatus]("Active", value = UserStatus.Active), EnumValue[UserStatus]("Paused", value = UserStatus.Paused) ) ) enum UserStatus { Active Paused } sealed abstract class UserStatus(val value: Int) object UserStatus { case object Active extends UserStatus(1) case object Paused extends UserStatus(2) } 50

Slide 51

Slide 51 text

&OVN5ZQF΋ϚΫϩͰ val userStatusType: EnumType[UserStatus] = derive.deriveEnumType[UserStatus]() enum UserStatus { Active Paused } sealed abstract class UserStatus(val value: Int) object UserStatus { case object Active extends UserStatus(1) case object Paused extends UserStatus(2) } 51

Slide 52

Slide 52 text

w &OVN5ZQF΋ϚΫϩͰಋग़Ͱ͖Δ w ATFBMFEAͱAPCKFDUAͷ૊Έ߹Θͤ &OVN5ZQF΋ϚΫϩͰ val userStatusType: EnumType[UserStatus] = derive.deriveEnumType[UserStatus]() sealed abstract class UserStatus(val value: Int) object UserStatus { case object Active extends UserStatus(1) case object Paused extends UserStatus(2) } 52

Slide 53

Slide 53 text

w ީิʹͳΔܕ 0CKFDU5ZQF Λ-JTUͰ༩͑Δ 6OJPO5ZQF union Plan = Free | Standard | Enterprise object Plan { case class Free(startDate: ZonedDateTime) case class Standard(startDate: ZonedDateTime) case class Enterprise(userLimit: Int, startDate: ZonedDateTime) } implicit val planType: UnionType[Ctx] = { val free = derive.deriveObjectType[Ctx, Plan.Free]() val standard = derive.deriveObjectType[Ctx, Plan.Standard]() val enterprise = derive.deriveObjectType[Ctx, Plan.Enterprise]() UnionType("Plan", types = free :: standard :: enterprise :: Nil) } 53

Slide 54

Slide 54 text

w ೖྗ஋ͱͯ͠+40/Λड͚औΔͨΊͷ΍ͭ w +40/Ͱड͚औͬͯ4DBMBͷΦϒδΣΫτʹม׵ *OQVU0CKFDU5ZQF mutation { UpdateTask(attributes: { taskId: "t1", taskName: "newName" }) { id } } case class UpdateTaskParam(taskId: String, taskName: Option[String], taskDescription: Option[String], assignedTo: Option[String]) 54

Slide 55

Slide 55 text

w ͍ͭ͜΋ϚΫϩͰࣗಈಋग़ग़དྷΔ *OQVU0CKFDU5ZQF case class UpdateTaskParam(taskId: String, taskName: Option[String], taskDescription: Option[String], assignedTo: Option[String]) 55 implicit val paramJson = jsonFormat4(UpdateTaskParam.apply) val param = derive.deriveInputObjectType[UpdateTaskParam]() mutation { UpdateTask(attributes: { taskId: "t1", taskName: "newName" }) { id } }

Slide 56

Slide 56 text

w &YFDVUPSʹεΩʔϚɺ࣮ߦ͍ͨ͠ΫΤϦɺ$POUFYU ͳͲΛ༩͑Δͱ+40/Λฦͯ͘͠ΕΔ (SBQI2-ͷ࣮ߦ val result: Future[JsValue] = Executor.execute[Ctx, Unit, JsObject]( schema, document, context ) 56 w ఆٛͨ͠(SBQI2-εΩʔϚ w ϦΫΤετͷ+40/Λύʔεͨ݁͠Ռ w ࣮ߦ͢ΔͨΊͷDPOUFYU

Slide 57

Slide 57 text

4DBMBͰͷ(SBQI2- w 4DBMBͱ૬ੑόπάϯ w ڧྗͳܕγεςϜͰ(SBQI2-ͷܕΛදݱ w ϚΫϩͰϘΠϥʔϓϨʔτ΋࡟ݮ w σϑΥϧτOPOOVMM࠷ߴ w 0QUJPOΛ͚ͭͳ͍ͱOVMMBCMFʹͳΒͳ͍ w +40/ͱͷ૬ޓม׵ w DBTFDMBTT͕͋ΔͷͰࣗಈಋग़Ͱ͖Δ w TBOHSJBͷ͓͔͛Ͱे෼ઓ͑Δ 57

Slide 58

Slide 58 text

(SBQI2-ͷ࣮ફ 58

Slide 59

Slide 59 text

(SBQI2-Ͱͷ࣮ફฤ w (SBQI2-Λ4DBMB4BOHSJBͰͲ͏࣮૷͍ͯ͠Δ͔ w ͋͘·Ͱ͜͏΍࣮ͬͯફ͍ͯ͠Δɺͱ͍͏࿩ w ͍Ζ͍Ζ೰ΈϙΠϯτ΋ଟ͍ w (SBQI2-ͳΒͰ͸ͷ೰Έ΋͋Δ w খ͍͞΋ͷ͔Βେ͖͍΋ͷ·Ͱ͍Ζ͍Ζ 59

Slide 60

Slide 60 text

(SBQI2-Ͱͷ࣮ફฤ w ΞʔΩςΫνϟ w εΩʔϚͷઃܭ w $POUFYUΛͲ͏͢Δ͔ w ೝূɺೝՄΛͲ͏͢Δ͔ w τϥϯβΫγϣϯ w ΤϥʔϋϯυϦϯά w /໰୊ w FUD 60

Slide 61

Slide 61 text

ΞʔΩςΫνϟ 61

Slide 62

Slide 62 text

ΞʔΩςΫνϟ w ϓϩάϥϛϯάϨϕϧͰͷઃܭ w (SBQI2-ʹͲ͜·Ͱ੹຿Λ೚ͤΔ͔ w ͲΜͳײ͡Ͱ࡞Δͷ͕͍͍ͷ͔ 62

Slide 63

Slide 63 text

ΞʔΩςΫνϟ HTTP GraphQL Business Logic Database Access 63

Slide 64

Slide 64 text

w (SBQI2-͸)551ϨΠϠͱϏδωεϩδοΫͷؒ ΛऔΓ࣋ͭଘࡏͱͯ͠ઃܭ͢Δͷ͕ྑ͍ w (SBQI2-͸͋͘·Ͱ*'ͱͯ͠ར༻͢Δ ΞʔΩςΫνϟ HTTP GraphQL Business Logic Database Access 64

Slide 65

Slide 65 text

w (SBQI2-࡞ऀͷ-FF#ZSPOࢯ΋ݴ͍ͬͯΔ w ೝূೝՄɺΩϟογϡ΍%#ΫΤϦͳͲ͸ (SBQI2-ϨΠϠͰ͸ͳͦ͘ͷԼͰ΍Δ΂͖ w ަ׵Մೳੑ্͕͕ͬͯকདྷ໾ʹཱͭ ΞʔΩςΫνϟ 65 https://www.youtube.com/watch?v=zVNrqo9XGOs

Slide 66

Slide 66 text

w (SBQI2-ͷར఺͸2VFSZʹ͋ΒΘΕΔ w ΫϥΠΞϯτͷཁٻʹԠ͑Δॊೈͳ2VFSZ w .VUBUJPO͸ਖ਼௚ͦ͜·Ͱʜ ΞʔΩςΫνϟ 66

Slide 67

Slide 67 text

w $234 w $PNNBOE2VFSZ3FTQPOTJCJMJUZ4FHSFHBUJPO ΞʔΩςΫνϟ 67 https://docs.microsoft.com/ja-jp/azure/architecture/guide/architecture-styles/cqrs

Slide 68

Slide 68 text

ΞʔΩςΫνϟ HTTP GraphQL Business Logic Database Access 68

Slide 69

Slide 69 text

ΞʔΩςΫνϟ 69 HTTP GraphQL UseCase/Application Database Access(DAO) Query Mutation Domain 3FQPTJUPSZ Resolver

Slide 70

Slide 70 text

w $234Λ؆ུԽͯ͠࠾༻͍ͯ͠Δ w ߋ৽ܥ͸%%%Ͱ͖ͬͪΓϞσϦϯά࣮૷ w ࢀরܥ͸TFMFDUͨ͠σʔλΛՃ޻͢Δ͚ͩ w (SBQI2-ͷ2VFSZ͸ͲΜͳσʔλΛඞཁͱ͞ΕΔ ͔ɺݺͼग़͞ΕΔ·ͰΘ͔Βͳ͍ w %%%3FQPTJUPSZ͸ݫ͍͠ w ఘΊ΋େࣄ w %BUB"DDFTT0CKFDUΛ࢖͏ ΞʔΩςΫνϟ 70

Slide 71

Slide 71 text

w ͱ͸͍͑2VFSZܥͷSFTPMWFͰ%"0Λ௚઀ݺͿͱɺ %"0ЋͳॲཧΛͨ͘͠ͳͬͨ࣌ʹࠔΔ w ΩϟογϡೖΕͨΓϖʔδωʔγϣϯͨ͠Γ w 3FTPMWFS૚Λ༻ҙ͓ͯ͘͠ͱྑ͍ w ಺෦Ͱ͸%"0Ћఔ౓ w SFTPMWFؔ਺͔Β࢖ΘΕΔ ΞʔΩςΫνϟ 71

Slide 72

Slide 72 text

w 3FTPMWFSͷ࣮૷ྫ ΞʔΩςΫνϟ 72 Field("users", ListType(userType), resolve = { ctx => UserResolver.all()(ctx.ctx) }) object UserResolver { def all()(implicit ctx: GraphQLContext): Future[Seq[User]] = { withCache("user#all") { UserDao.findAll() } } }

Slide 73

Slide 73 text

εΩʔϚ 73

Slide 74

Slide 74 text

εΩʔϚ w ΫϥΠΞϯτʹͲΜͳεΩʔϚΛఏڙ͢Δ͔ w 3&45GVMͳ"1*ʹ͓͚Δ63-ઃܭʹ͍ۙ w ·͞ʹϦιʔεઃܭ w εΩʔϚͷେ͖ͳ෼͚ํ w ࢀরܥ͸2VFSZ w ߋ৽ܥ͸.VUBUJPO 74

Slide 75

Slide 75 text

εΩʔϚ 2VFSZ w SPPUϨϕϧ͸ඇৗʹॏཁ w ʮ(SBQI2-͸ΫϥΠΞϯτ͕ඞཁͳσʔλ͚ͩ Λબ୒ͯ͠औಘ͢Δ͜ͱ͕ग़དྷΔʯͷى఺ w ͔͜͜Βؔ࿈Λ୧͍ͬͯ͘͜ͱʹͳΔ type Query { projects: [Project!]! tasks(status: TaskStatus): [Task!]! users(arg: UserSearchCondition): [User!]! } 75

Slide 76

Slide 76 text

εΩʔϚ 2VFSZ w AWJFXFSA w ϦΫΤετ͖ͯͨ͠Ϣʔβࣗ਎ʹඥͮ͘৘ใ w ࣗ෼ࣗ਎ΛOPOOVMMͰఏڙͨ͠Γ type Query { projects: [Project!]! tasks: [Task!]! users: [User!]! viewer: ViewerQuery! } type ViewerQuery { self: User! assignedTasks: [Task!]! belongsTo: [Project!]! } 76

Slide 77

Slide 77 text

εΩʔϚ 2VFSZ w AWJFXFSA w ϦΫΤετ͖ͯͨ͠Ϣʔβࣗ਎ʹඥͮ͘৘ใ w ࣗ෼ࣗ਎ΛOPOOVMMͰఏڙͨ͠Γ w (JUIVCWΛύΫΔ 77

Slide 78

Slide 78 text

εΩʔϚ .VUBUJPO w શͯSPPUϨϕϧʹฒ΂Δͷ͕Φεεϝ w ωετͨ͠.VUBUJPO͸࣮ߦॱং͕อূ͞Εͳ͍ mutation { CreateTask(…) { id } UpdateTask(…) { id } // CreateTaskͷޙʹ࣮ߦ͞ΕΔ } mutation { Task { Create(…) { id } Update(…) { id } // Createͷޙʹ࣮ߦ͞ΕΔอূ͸ͳ͍ } } 78

Slide 79

Slide 79 text

εΩʔϚ .VUBUJPO w "SHVNFOUͱ*OQVU0CKFDU5ZQF͸͓޷ΈͰྑͦ͞͏ w 63-ύϥϝʔλPS+40/ͷΠϝʔδ w *OQVU0CKFDU5ZQF͸DBTFDMBTTʹύʔεग़དྷͯศར 79 *OQVU0CKFDU5ZQF "SHVNFOU

Slide 80

Slide 80 text

όʔδϣχϯά 80

Slide 81

Slide 81 text

όʔδϣχϯά w Α͋͘Δޡղ w ʮ(SBQI2-ͩͱόʔδϣχϯά͠ͳͯ͘Α͍ʯ 81

Slide 82

Slide 82 text

όʔδϣχϯά w "1*ͷύεͱͯ͠όʔδϣχϯά͸ෆཁ w WHSBQIRM w WHSBQIRM 82 "

Slide 83

Slide 83 text

όʔδϣχϯά w εΩʔϚͷόʔδϣχϯά͸ඞཁ w ϑΟʔϧυͷ௥ՃͷΈɺͩͱෆཁ w มߋ͸!EFQFSFDBUFE͚ͭͯɺมߋޙͷϑΟʔϧ υผ໊Ͱ௥Ճɺ͠͹Βͨ͘͠ΒมߋલͷΛফ͢ w ࡟আ͸!EFQFSFDBUFE͚ͭͯ͠͹Βͨ͘͠Βফ͢ w %#ͷεΩʔϚͱಉ͡Α͏ͳϑϩʔ w ϑΟʔϧυ໊͸ඇৗʹॏཁ w มߋͮ͠Β͍ɺͱ͍͏ͷΛ೦಄ʹஔ͍͓ͯ͘ 83

Slide 84

Slide 84 text

όʔδϣχϯά w 'JFMEʹAEFQSFDBUFE3FBTPOAΛ༩͑Δ 84 Field("name", StringType, resolve = { ctx => ctx.value.name }), Field("displayName", StringType, deprecationReason = Some("use `name` instead"), resolve = { ctx => ctx.value.name }),

Slide 85

Slide 85 text

(SBQI2-ʹ͓͚Δ $POUFYU 85

Slide 86

Slide 86 text

val userType: ObjectType[Ctx, User] = ObjectType[Ctx, User]( "User", fields[Ctx, User]( Field("id", IDType, resolve = { _.value.id }), Field("name", StringType, resolve = { ctx: Context[Ctx, User] => ctx.value.name }), Field("status", userStatusType, resolve = { _.value.status }), Field("projects", ListType(projectType), resolve = { ctx => ProjectResolver.byIds(ctx.value.projectIds)(ctx.ctx) }) ) ) 0CKFDU5ZQF 86

Slide 87

Slide 87 text

(SBQI2-ʹ͓͚Δ$POUFYU w (SBQI2-ͷܕʹ͓͚ΔSFTPMWFΛఆٛ͢Δࡍʹొ৔ ͢Δ$POUFYUʹԿΛ࣋ͨͤΔ͔ w ʮ$POUFYUʯͱ͍͏໊લ͕೉͍͠ w DPOUFYUVBMͳ৘ใͬͯԿʁ 87

Slide 88

Slide 88 text

(SBQI2-ʹ͓͚Δ$POUFYU w ྫϩάΠϯதͷϢʔβ΍%#΁ͷΞΫηεΛ࣋ͭ context"WBMVFXIJDIJTQSPWJEFEUPFWFSZSFTPMWFSBOEIPMET JNQPSUBOUDPOUFYUVBMJOGPSNBUJPOMJLFUIFDVSSFOUMZMPHHFEJOVTFS PSBDDFTTUPBEBUBCBTF IUUQTHSBQIRMPSHMFBSOFYFDVUJPOSPPUpFMETSFTPMWFST 88

Slide 89

Slide 89 text

(SBQI2-ʹ͓͚Δ$POUFYU w %#ΞΫηεՄೳͳ4FSWJDF3FQPTJUPSZͳͲ 89 IUUQTTBOHSJBHSBQIRMPSHMFBSOJOQVUBOEDPOUFYUPCKFDUT

Slide 90

Slide 90 text

(SBQI2-ʹ͓͚Δ$POUFYU w ͳΔ΄Ͳɺ%"0΍4FSWJDF΍3FQPTJUPSZΛ$POUFYU ͔ΒΞΫηεग़དྷΔΑ͏ʹͨ͠Βྑ͍Μͩͳʂ 90

Slide 91

Slide 91 text

(SBQI2-ʹ͓͚Δ$POUFYU w ͳΔ΄Ͳɺ%"0΍4FSWJDF΍3FQPTJUPSZΛ$POUFYU ͔ΒΞΫηεग़དྷΔΑ͏ʹͨ͠Βྑ͍Μͩͳʂ 91 ͓͢͢Ί͠ͳ͍

Slide 92

Slide 92 text

(SBQI2-ʹ͓͚Δ$POUFYU w %"0͕શͯͷϑΟʔϧυ͔Β$POUFYUܦ༝Ͱ޷͖ উखʹΞΫηεग़དྷΔΑ͏ʹͳͬͯ͠·ͬͯϨΠ Ϡʔ͕յΕΔ w SFTPMWFؔ਺ͰશͯΛ΍ͬͯ͠·͑Δঢ়ଶʹ w ݁Ռͱͯ͠(SBQI2-ϨΠϠ͕ް͘ͳΔ 92

Slide 93

Slide 93 text

(SBQI2-ʹ͓͚Δ$POUFYU w $POUFYU͸ಉҰϦΫΤετ಺Ͱڞ௨ͯ͠ࢀরՄೳ w (SBQI2-ΫΤϦͷ࣮ߦ୯Ґ ϦΫΤετ Ͱڞ௨͠ ͯࢀর͍ͨ͠΋ͷΛ࣋ͨͤΔ΂͖ w ϩάΠϯϢʔβ w τϥϯβΫγϣϯ w &YFDVUJPO$POUFYU w FUD 93

Slide 94

Slide 94 text

w $POUFYUͷ࣮૷ྫ w ϩάΠϯϢʔβ w τϥϯβΫγϣϯ %#4FTTJPO w &YFDVUJPO$POUFYU class GraphQLContext( userOpt: Option[User], session: DBSession, ec: ExecutionContext ) (SBQI2-ʹ͓͚Δ$POUFYU 94

Slide 95

Slide 95 text

ೝূೝՄ 95

Slide 96

Slide 96 text

ೝূೝՄ w 8FCΞϓϦέʔγϣϯͳΒେମ͋Δ΍ͭ w ೝূ "VUIFOUJDBUJPO w ୭͔Λಛఆ͢Δ w ೝՄ "VUIPSJ[BUJPO w ݖݶΛ֬ೝ͢Δ w (SBQI2-ͱͲ͏૊Έ߹ΘͤΔ΂͖͔ɺͱ͍͏࿩ 96

Slide 97

Slide 97 text

ೝূ w ೝূͨ݁͠Ռ͸શମͰ࢖͍ճ͍ͨ͠ w ෳ਺ͷ2VFSZ.VUBUJPOΛಉ࣌ʹ࣮ߦ͢Δࡍʹɺ ผʑʹೝূΛ͢Δͷ͸ྲྀੴʹڐ༰Ͱ͖ͳ͍ w ೝূॲཧͦͷ΋ͷ͸(SBQI2-ͱ͸ؔ܎ͳ͍ w $PPLJF࢖͓͏͕+85࢖͓͏͕͓޷͖ʹ 97

Slide 98

Slide 98 text

ೝূ w (SBQI2-ͷ$POUFYUΛ࡞੒͢ΔࡍʹೝূΛߦ͏ w ೝূ݁ՌΛ$POUFYUʹೖΕͯ͠·͑͹ڞ༗ग़དྷΔ 98 object GraphQLContext { def create(userIdOpt: Option[String])( implicit ec: ExecutionContext, s: DBSession): GraphQLContext = { userIdOpt.flatMap { userId => authenticate(userId).map { user => new GraphQLContext(Some(user), ec, s) } } getOrElse { new GraphQLContext(None, ec, s) } } private def authenticate(userId: String): Option[User] = ??? } GBDUPSZͰೝূͭͭ͠ੜ੒

Slide 99

Slide 99 text

ೝূ w ೝূΛඞཁͱ͢Δ2VFSZ.VUBUJPOΛ࣮૷͢Δ w ೝূॲཧͦͷ΋ͷ͸(SBQI2-ͱ͸੾Γ཭͢ w .JEEMFXBSF͕࢖͏ͱڧ੍Ͱ͖ͯศར w ΫΤϦ࣮ߦͷCFGPSFBGUFSʹॲཧΛࠩ͠ࠐΊΔ w TBOHSJBFYFDVUJPO഑ԼͷΫϥεΛར༻͢Δ w 'JFME5BH w .JEEMFXBSF.JEEMFXBSF#FGPSF'JFME 99

Slide 100

Slide 100 text

w ೝূඞਢʹ͍ͨ͠pFMEͷఆٛʹUBHΛ෇༩͢Δ w &YFDVUPSFYFDVUFʹNJEEMFXBSFͱͯ͠ "VUIFOUJDBUJPO'JMUFSΛ༩͑Ε͹ಈ͘ ೝূ val self: Field[Ctx, User] = Field( "self", userType, tags = Middlewares.RequireAuthentication :: Nil, resolve = { ctx => ctx.ctx.loggedInUser } ) ೝূඞਢͳ͜ͱΛλάͰ໌ࣔ Executor.execute[Ctx, Unit, JsObject]( schema, queryDocument, context, middleware = AuthenticationFilter :: Nil ) 100

Slide 101

Slide 101 text

ೝূ type Ctx = GraphQLContext case object RequireAuthentication extends FieldTag object AuthenticationFilter extends MiddlewareBeforeField[Ctx] { override type QueryVal = Unit override type FieldVal = Unit private type MCtx = MiddlewareQueryContext[Ctx, _, _] override def beforeQuery(context: MCtx) = () override def afterQuery(queryVal: QueryVal, context: MCtx) = () override def beforeField(queryVal: QueryVal, mctx: MCtx, ctx: Context[Ctx, _]) = { if (ctx.field.tags contains RequireAuthentication) { if (!ctx.ctx.isLoggedIn) { throw AuthenticationError("must logged in!") } } continue } } 101

Slide 102

Slide 102 text

type Ctx = GraphQLContext case object RequireAuthentication extends FieldTag object AuthenticationFilter extends MiddlewareBeforeField[Ctx] { override type QueryVal = Unit override type FieldVal = Unit private type MCtx = MiddlewareQueryContext[Ctx, _, _] override def beforeQuery(context: MCtx) = () override def afterQuery(queryVal: QueryVal, context: MCtx) = () override def beforeField(queryVal: QueryVal, mctx: MCtx, ctx: Context[Ctx, _]) = { if (ctx.field.tags contains RequireAuthentication) { if (!ctx.ctx.isLoggedIn) { throw AuthenticationError("must logged in!") } } continue } } ೝূ ೝূඞਢͳϑΟʔϧυʹ෇༩͢Δ5BH 102

Slide 103

Slide 103 text

type Ctx = GraphQLContext case object RequireAuthentication extends FieldTag object AuthenticationFilter extends MiddlewareBeforeField[Ctx] { override type QueryVal = Unit override type FieldVal = Unit private type MCtx = MiddlewareQueryContext[Ctx, _, _] override def beforeQuery(context: MCtx) = () override def afterQuery(queryVal: QueryVal, context: MCtx) = () override def beforeField(queryVal: QueryVal, mctx: MCtx, ctx: Context[Ctx, _]) = { if (ctx.field.tags contains RequireAuthentication) { if (!ctx.ctx.isLoggedIn) { throw AuthenticationError("must logged in!") } } continue } } ೝূ 0CKFDU5ZQF౳ͷpFMEʹࠩ͠ࠐΉॲཧ 103

Slide 104

Slide 104 text

type Ctx = GraphQLContext case object RequireAuthentication extends FieldTag object AuthenticationFilter extends MiddlewareBeforeField[Ctx] { override type QueryVal = Unit override type FieldVal = Unit private type MCtx = MiddlewareQueryContext[Ctx, _, _] override def beforeQuery(context: MCtx) = () override def afterQuery(queryVal: QueryVal, context: MCtx) = () override def beforeField(queryVal: QueryVal, mctx: MCtx, ctx: Context[Ctx, _]) = { if (ctx.field.tags contains RequireAuthentication) { if (!ctx.ctx.isLoggedIn) { throw AuthenticationError("must logged in!") } } continue } } ೝূ 3FRVJSF"VUIFOUJDBUJPOλά͕෇͍ͯΔͷʹ ϩάΠϯͯ͠ͳ͍৔߹͸ྫ֎Λૹग़ 104

Slide 105

Slide 105 text

ೝՄ w ೝՄ͸ΞϓϦέʔγϣϯ૚Ͱߦ͏ w (SBQI2-ʹ͸ؔ܎ͳ͍࿩ͳͷͰׂѪ 105

Slide 106

Slide 106 text

%#τϥϯβΫγϣϯ 106

Slide 107

Slide 107 text

%#τϥϯβΫγϣϯ w Ͳͷ୯ҐͰτϥϯβΫγϣϯΛ࣮ߦ͢Δ͔ w ͍ͭ#FHJO͍ͯͭ͠$PNNJU3PMMCBDL͢Δ͔ w (SBQI2-ΫΤϦ͸ෳ਺ͷ2VFSZ.VUBUJPOΛ·ͱ Ί࣮ͯߦ͢Δ͜ͱ͕ग़དྷΔ w ಛʹ.VUBUJPO͸ߋ৽ܥͳͷͰτϥϯβΫγϣϯ Λผʑʹ͍ͨ͠ͱ͍͏ཉٻ͸͋Δ͸ͣ 107

Slide 108

Slide 108 text

%#τϥϯβΫγϣϯ w ݁࿦ɺϦΫΤετʹରͯ͠τϥϯβΫγϣϯ w ຊ౰ʹτϥϯβΫγϣϯΛผʹ͍ͨ͠ૢ࡞ΛϦ ΫΤετʹ৐ͤͯ·ͱΊ࣮ͯߦ͢Δ͜ͱ͕͋Δ ͩΖ͏͔ʁ 108

Slide 109

Slide 109 text

%#τϥϯβΫγϣϯ w τϥϯβΫγϣϯͷ੹຿͕(SBQI2-ϨΠϠʹʜ w $POUFYUʹ࣋ͨͤͯΫΤϦؒͰڞ༗͢Δ w ઃܭతʹ͸ͪΐͬͱඍົͳؾ࣋ͪ w ԿΛ༏ઌ͢Δ͔Ͱܾఆ͢Δ΂͖ 109

Slide 110

Slide 110 text

ΤϥʔϋϯυϦϯά 110

Slide 111

Slide 111 text

ΤϥʔϋϯυϦϯά w (SBQI2-Ͱ͸εςʔλείʔυ͸جຊతʹ w Τϥʔ͸ϨεϙϯεϘσΟʹೖΕͯฦ٫͞ΕΔ 111

Slide 112

Slide 112 text

ΤϥʔϋϯυϦϯά w ෳ਺ͷ2VFSZ.VUBUJPOΛ࣮ߦͨ͠ࡍʹɺͲΕ͔ ͭͰ΋ࣦഊͨ͠Βશମ͕Τϥʔʹͳͬͯ͠·͏ ͱ͍͏໰୊͕͋Δ w ྫɿτοϓϖʔδͷදࣔʹඞཁͳϦιʔεΛ· ͱΊͯऔಘͨ͠ΒɺҰ෦෼͚ͩऔಘʹࣦഊͨ͠ ͚ͩͰશମ͕ਅͬനʹͳΔ w ϚΠΫϩαʔϏε࣌୅ʹ͙ͦΘͳ͍ײ w #''ʹ࢖͍ਏ͍ʜ 112

Slide 113

Slide 113 text

ΤϥʔϋϯυϦϯά w ͍Ζ͍Ζબ୒ࢶ͸͋Δ w શ෦औಘͰ͖ͳ͍ͱҙຯແ͍୯ҐͰΫΤϦൃߦ w গʑࣦഊͯ͠΋ϦτϥΠ͢Ε͹͑͑΍Μ w FUD w ੒ޭࣦഊΛಉډͤ͞Δͷ͸͔ͳΓେมͦ͏ w 6OJPO5ZQFʹͯ͠੒ޭࣦഊΛදݱ͢Δͱ͔ʁ w SFTPMWFͰྫ֎͕౤͛ΒΕͳ͍Α͏ʹέΞ͕ඞཁ 113

Slide 114

Slide 114 text

ΤϥʔϋϯυϦϯά w ͰɺͲ͏͍ͯ͠Δ͔ w Ұ෦ͷΤϥʔΛશମͷΤϥʔͱ͍ͯ͠Δ w ࣮૷ίετͱͷ݉Ͷ߹͍ w Ұ෦Ͱ΋ࣦഊ͢ΔͷͬͯԿ͔͕͓͔͍͠ w #''ͳΒಛఆͷΞϓϦ͕ෆ௨ʹͳΔՄೳੑ΋ w ୯ҰΞϓϦͳΒ૝ఆ͢Δඞཁͳ͍ 114

Slide 115

Slide 115 text

ΤϥʔϋϯυϦϯά w Τϥʔͷछྨ͚ͩΘ͔ΔΑ͏ʹ w ϦΫΤετෆਖ਼αʔόΤϥʔೝূΤϥʔ sealed abstract class ErrorType(val value: String) object ErrorType { case object BadRequest extends ErrorType("BadRequest") case object ServerError extends ErrorType("ServerError") case object AuthError extends ErrorType("AuthError") } implicit val error = derive.deriveEnumType[ErrorType]() 115

Slide 116

Slide 116 text

ΤϥʔϋϯυϦϯά w Τϥʔͷछྨ͚ͩΘ͔ΔΑ͏ʹ w ϦΫΤετෆਖ਼αʔόΤϥʔೝূΤϥʔ sealed abstract class ErrorType(val value: String) object ErrorType { case object BadRequest extends ErrorType("BadRequest") case object ServerError extends ErrorType("ServerError") case object AuthError extends ErrorType("AuthError") } implicit val error = derive.deriveEnumType[ErrorType]() def handleException(m: ResultMarshaller, msg: String, tp: ErrorType) = HandledException( msg, additionalFields = Map("type" -> m.enumNode(tp.value, error.name)), addFieldsInExtensions = false, addFieldsInError = true ) )BOEMFE&YDFQUJPOΛ࢖͑͹ (SBQI2-ͬΆ͍ΤϥʔϝοηʔδΛฦ٫Ͱ͖Δ 116

Slide 117

Slide 117 text

ΤϥʔϋϯυϦϯά w Τϥʔͷछྨ͚ͩΘ͔ΔΑ͏ʹ w ϦΫΤετෆਖ਼αʔόΤϥʔೝূΤϥʔ sealed abstract class ErrorType(val value: String) object ErrorType { case object BadRequest extends ErrorType("BadRequest") case object ServerError extends ErrorType("ServerError") case object AuthError extends ErrorType("AuthError") } implicit val error = derive.deriveEnumType[ErrorType]() def handleException(m: ResultMarshaller, msg: String, tp: ErrorType) = HandledException( msg, additionalFields = Map("type" -> m.enumNode(tp.value, error.name)), addFieldsInExtensions = false, addFieldsInError = true ) 117

Slide 118

Slide 118 text

ΤϥʔϋϯυϦϯά w ϋϯυϦϯά͢Δ΂͖Τϥʔ͕ଟ͍ w +40/ͷTZOUBYΤϥʔ w ΫΤϦͷTZOUBYΤϥʔɺWBMJEBUJPOΤϥʔ w ϏδωεϩδοΫ্ͷΤϥʔ w ͦͷଞɺUISPX͞ΕͨΤϥʔ w Ͳ͏΍ͬͯϋϯυϦϯά͢Δʁ w &YDFQUJPO)BOEMFSΛؤு࣮ͬͯ૷͢Δ w &YFDVUPSFYFDVUFͷSFDPWFSͰ΋ؤுΔ 118

Slide 119

Slide 119 text

ΤϥʔϋϯυϦϯά private val exceptionHandler = { def handleException(m: ResultMarshaller, msg: String, tp: ErrorType) = HandledException( msg, additionalFields = Map("type" -> m.enumNode(tp.value, errorType.name)), addFieldsInExtensions = false, addFieldsInError = true ) val onException: PartialFunction[(ResultMarshaller, Throwable), HandledException] = { case (m, qa: QueryAnalysisError) => logger.warn(s"QueryAnalysisError occurred. msg = ${qa.getMessage}") handleException(m, qa.getMessage, ErrorType.BadRequest) case (m, se: SyntaxError) => logger.info(s"SyntaxError occurred. msg = ${se.getMessage()}") handleException(m, se.getMessage, ErrorType.BadRequest) case (m, ewr: ErrorWithResolver) => logger.error(s"ErrorWithResolver occurred. msg = ${ewr.getMessage}", ewr) handleException(m, ewr.getMessage, ErrorType.ServerError) case (m, AuthenticationError(msg)) => logger.info(s"AuthenticationError occurred. msg = ${msg}") handleException(m, msg, ErrorType.BadRequest) case (m, NotFoundException(msg)) => logger.info(s"NotFoundException occurred. msg = ${msg}") handleException(m, msg, ErrorType.BadRequest) case (m, t: Throwable) => logger.error(s"unknown server error occurred. msg = ${t.getMessage}", t) handleException(m, t.getMessage, ErrorType.ServerError) } val onViolation: PartialFunction[(ResultMarshaller, Violation), HandledException] = { case (m, v) => logger.warn(s"Violation error occurred. msg = ${v.errorMessage}") handleException(m, v.errorMessage, ErrorType.BadRequest) } val onUserFacingError : PartialFunction[(ResultMarshaller, UserFacingError), HandledException] = { case (m, v: WithViolations) => logger.warn(s"WithViolations occurred. msg = ${v.getMessage()}") handleException(m, v.getMessage, ErrorType.BadRequest) case (m, ie: InternalError) => logger.error(s"InternalError occurred. msg = ${ie.getMessage()}", ie) handleException(m, ie.getMessage, ErrorType.ServerError) case (m, ee: ExecutionError) => logger.error(s"ExecutionError occurred. msg = ${ee.getMessage()}", ee) handleException(m, ee.getMessage, ErrorType.ServerError) case (m, ufe) => logger.warn(s"UserFacingError occurred. msg = ${ufe.getMessage()}") handleException(m, ufe.getMessage, ErrorType.BadRequest) } ExceptionHandler(onException, onViolation, onUserFacingError) } Future.fromTry(QueryParser.parse(document)) zip GraphQLContext.create(userIdOpt) flatMap { case (queryDocument: Document, context: GraphQLContext) => val result: Future[JsValue] = Executor.execute[Ctx, Unit, JsObject]( schema, queryDocument, context, exceptionHandler = exceptionHandler, operationName = operation, variables = vars, middleware = Middlewares.value ) result .map { jsValue => OK -> jsValue } .recover { case error: QueryAnalysisError => BadRequest -> error.resolveError case error: ErrorWithResolver => InternalServerError -> error.resolveError } } recover { case v: SyntaxError => BadRequest -> new Exception(v.getMessage()) with ErrorWithResolver with WithViolations { override def exceptionHandler: ExceptionHandler = GraphQLServer.exceptionHandler override def violations: Vector[Violation] = Vector.empty }.resolveError case v: ValidationError => BadRequest -> v.resolveError case NonFatal(t) => InternalServerError -> InternalError(t, GraphQLServer.exceptionHandler).resolveError } 119

Slide 120

Slide 120 text

ΤϥʔϋϯυϦϯά private val exceptionHandler = { def handleException(m: ResultMarshaller, msg: String, tp: ErrorType) = HandledException( msg, additionalFields = Map("type" -> m.enumNode(tp.value, errorType.name)), addFieldsInExtensions = false, addFieldsInError = true ) val onException: PartialFunction[(ResultMarshaller, Throwable), HandledException] = { case (m, qa: QueryAnalysisError) => logger.warn(s"QueryAnalysisError occurred. msg = ${qa.getMessage}") handleException(m, qa.getMessage, ErrorType.BadRequest) case (m, se: SyntaxError) => logger.info(s"SyntaxError occurred. msg = ${se.getMessage()}") handleException(m, se.getMessage, ErrorType.BadRequest) case (m, ewr: ErrorWithResolver) => logger.error(s"ErrorWithResolver occurred. msg = ${ewr.getMessage}", ewr) handleException(m, ewr.getMessage, ErrorType.ServerError) case (m, AuthenticationError(msg)) => logger.info(s"AuthenticationError occurred. msg = ${msg}") handleException(m, msg, ErrorType.BadRequest) case (m, NotFoundException(msg)) => logger.info(s"NotFoundException occurred. msg = ${msg}") handleException(m, msg, ErrorType.BadRequest) case (m, t: Throwable) => logger.error(s"unknown server error occurred. msg = ${t.getMessage}", t) handleException(m, t.getMessage, ErrorType.ServerError) } val onViolation: PartialFunction[(ResultMarshaller, Violation), HandledException] = { case (m, v) => logger.warn(s"Violation error occurred. msg = ${v.errorMessage}") handleException(m, v.errorMessage, ErrorType.BadRequest) } val onUserFacingError : PartialFunction[(ResultMarshaller, UserFacingError), HandledException] = { case (m, v: WithViolations) => logger.warn(s"WithViolations occurred. msg = ${v.getMessage()}") handleException(m, v.getMessage, ErrorType.BadRequest) case (m, ie: InternalError) => logger.error(s"InternalError occurred. msg = ${ie.getMessage()}", ie) handleException(m, ie.getMessage, ErrorType.ServerError) case (m, ee: ExecutionError) => logger.error(s"ExecutionError occurred. msg = ${ee.getMessage()}", ee) handleException(m, ee.getMessage, ErrorType.ServerError) case (m, ufe) => logger.warn(s"UserFacingError occurred. msg = ${ufe.getMessage()}") handleException(m, ufe.getMessage, ErrorType.BadRequest) } ExceptionHandler(onException, onViolation, onUserFacingError) } Future.fromTry(QueryParser.parse(document)) zip GraphQLContext.create(userIdOpt) flatMap { case (queryDocument: Document, context: GraphQLContext) => val result: Future[JsValue] = Executor.execute[Ctx, Unit, JsObject]( schema, queryDocument, context, exceptionHandler = exceptionHandler, operationName = operation, variables = vars, middleware = Middlewares.value ) result .map { jsValue => OK -> jsValue } .recover { case error: QueryAnalysisError => BadRequest -> error.resolveError case error: ErrorWithResolver => InternalServerError -> error.resolveError } } recover { case v: SyntaxError => BadRequest -> new Exception(v.getMessage()) with ErrorWithResolver with WithViolations { override def exceptionHandler: ExceptionHandler = GraphQLServer.exceptionHandler override def violations: Vector[Violation] = Vector.empty }.resolveError case v: ValidationError => BadRequest -> v.resolveError case NonFatal(t) => InternalServerError -> InternalError(t, GraphQLServer.exceptionHandler).resolveError } ΍Δ͔͠ͳ͍ 120

Slide 121

Slide 121 text

/໰୊ 121

Slide 122

Slide 122 text

/໰୊ w (SBQI2-Ͱ͸ϦΫΤετΛճʹݻఆͰ͖Δ w ؔ࿈Λ୧ͬͯඞཁͳσʔλΛऔಘ͢Δ w ݁Ռͱͯ͠/ͳ42-͕ൃߦ͞Ε͕ͪ 122

Slide 123

Slide 123 text

/໰୊ QSPKFDU͕/ݸ͋Ε͹ VTFST΋/ճऔಘ͢Δɺ ·͞ʹ/ͳঢ়ଶ 123

Slide 124

Slide 124 text

/໰୊ w /Λճආ͢ΔͨΊͷ࢓૊Έ͕ఏڙ͞Ε͍ͯΔ w %FGFSSFE3FTPMWFS w 'FUDIFS w SFTPMWF͢ΔͷΛ஗Βͤͯɺ·ͱΊͯऔಘͰ͖Δ΋ ͷΛ·ͱΊͯऔಘ͢ΔΑ͏ʹ͢Δ w pOE#Z*E/ΛpOE#Z*ETʹ͢Δ͚ͩͳΠϝʔδ w Ωϟογϡͨ͠Γ΋Մೳ 124

Slide 125

Slide 125 text

object UserResolver { private implicit lazy val hasId: HasId[User, UserId] = HasId(_.id) val userFetcher: Fetcher[GraphQLContext, User, User, UserId] = { Fetcher.apply { (ctx: GraphQLContext, ids: Seq[UserId]) => UserDao.findByIds(ids)(ctx.ec) } } def byId(userId: UserId)( implicit ctx: GraphQLContext): DeferredValue[GraphQLContext, User] = { DeferredValue(userFetcher.defer(userId)) } } /໰୊ 125

Slide 126

Slide 126 text

object UserResolver { private implicit lazy val hasId: HasId[User, UserId] = HasId(_.id) val userFetcher: Fetcher[GraphQLContext, User, User, UserId] = { Fetcher.apply { (ctx: GraphQLContext, ids: Seq[UserId]) => UserDao.findByIds(ids)(ctx.ec) } } def byId(userId: UserId)( implicit ctx: GraphQLContext): DeferredValue[GraphQLContext, User] = { DeferredValue(userFetcher.defer(userId)) } } /໰୊ 126 4FR<6TFS*E>4FR<6TFS>ͳॲཧ

Slide 127

Slide 127 text

object UserResolver { private implicit lazy val hasId: HasId[User, UserId] = HasId(_.id) val userFetcher: Fetcher[GraphQLContext, User, User, UserId] = { Fetcher.apply { (ctx: GraphQLContext, ids: Seq[UserId]) => UserDao.findByIds(ids)(ctx.ec) } } def byId(userId: UserId)( implicit ctx: GraphQLContext): DeferredValue[GraphQLContext, User] = { DeferredValue(userFetcher.defer(userId)) } } /໰୊ 127 4FR<6TFS*E>4FR<6TFS>ͳॲཧΛ ϥοϓ͢Δ'FUDIFS

Slide 128

Slide 128 text

object UserResolver { private implicit lazy val hasId: HasId[User, UserId] = HasId(_.id) val userFetcher: Fetcher[GraphQLContext, User, User, UserId] = { Fetcher.apply { (ctx: GraphQLContext, ids: Seq[UserId]) => UserDao.findByIds(ids)(ctx.ec) } } def byId(userId: UserId)( implicit ctx: GraphQLContext): DeferredValue[GraphQLContext, User] = { DeferredValue(userFetcher.defer(userId)) } } /໰୊ 128 'FUDIFSEFGFSͰ*%Λ౉͢ͱ ͍͍ײ͡ʹ/Λ௵ͯ͘͠ΕΔ

Slide 129

Slide 129 text

/໰୊ 129 w ؔ࿈Λ୧Δ/Λղফ͢Δͷ΋༻ҙ͞Ε͍ͯΔ object UserResolver { private val usersByProjects: Relation[User, User, ProjectId] = Relation[User, ProjectId]("byProjects", { u: User => u.projectIds }) lazy val userFetcherByProject: Fetcher[GraphQLContext, User, User, UserId] = { Fetcher.relOnly { (ctx: GraphQLContext, rel: RelationIds[User]) => val ids: Seq[ProjectId] = rel.apply(usersByProjects) UserDao.findByProjectIds(ids)(ctx.ec) } } def findByProjectId(projectId: ProjectId)( implicit ctx: GraphQLContext): DeferredValue[GraphQLContext, Seq[User]] = { DeferredValue(userFetcherByProject.deferRelSeq(usersByProjects, projectId)) } }

Slide 130

Slide 130 text

·ͱΊ 130

Slide 131

Slide 131 text

·ͱΊ w (SBQI2-ศརͩ͠Φεεϝ w ۜͷ஄ؙͰ͸ͳ͍ ೋ౓໨ w ࣍ͷϓϩδΣΫτͰ΋࠾༻͍ͨ͠ w 3&45΍ͬͯͨࠒͱ೰ΉϙΠϯτ͕มΘΔ w Ͳ͕ͬͪྑ͍ɺͱ͍͏࿩Ͱ͸ͳ͍ w 4DBMBͱ΋૬ੑ͸ྑ͍ w 4BOHSJBΛ࢖͑͹े෼ઓ͑Δ 131

Slide 132

Slide 132 text

࣮ફͰ͖ͦ͏Ͱ͠ΐ͏͔ʁ 132

Slide 133

Slide 133 text

ࠓ೔ͷΰʔϧ ʮ࣍ͷϓϩδΣΫτͰ͸ ɹ(SBQI2-Λ 4DBMBͰ ΍Γ͍ͨʂʯ 133

Slide 134

Slide 134 text

࣮ફ(SBQI2-PO4DBMB 4DBMBؔ੢4VNNJU !QFUJUWJPMFU