Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Spring for GraphQLって実際どうなの?〜小規模スタートアップの事例紹介〜
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
koga yushi
June 06, 2025
Technology
470
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Spring for GraphQLって実際どうなの?〜小規模スタートアップの事例紹介〜
JJUG CCC Spring 2025登壇資料です。
koga yushi
June 06, 2025
Other Decks in Technology
See All in Technology
Browser Spec: Testing Hotwire with Ruby
takahashim
1
130
M5Stackで作る BLEマウス GPSログの世界
tomokusaba
PRO
0
440
事業成長とデータ活用を止めないためのマネジメントへ挑戦
hiracky16
0
400
Local CI × GitHub Actions
gogutan
3
230
ウォンテッドリーの最近のプロダクトと技術の紹介
stomk
0
130
スプシの手作業からの解脱 ~更新はClaude Codeに、見せ方と使い方はGASアプリに任せる~
kentarofujii
0
160
ソフトウェア ファクトリー - 再現性のあるソフトウェアの工場づくり / Software factory
iktakahiro
7
5.1k
初海外で注意すべきこと12選!!!安全快適なre:invent生活にするために
yuuuuuuu168
1
180
Active Record Connection Adaptersにおける長年の課題に再び取り組む
yahonda
0
200
AI × アーキテクト 第1・2章|生成AIの課題とアーキテクトの素養
daidaidai
0
180
Claude の新機能を、 出た日に触って社内に届けるまで
rfdnxbro
1
190
Railsで挑む大量データ永続化 〜600億件を永続化するための設計とボトルネックとの戦い〜
occhi
1
780
Featured
See All Featured
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.5k
Accessibility Awareness
sabderemane
1
250
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
2
2.9k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.7k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
123
22k
Making the Leap to Tech Lead
cromwellryan
135
10k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
287
14k
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.8k
Rails Girls Zürich Keynote
gr2m
96
14k
Design in an AI World
tapps
1
350
Transcript
Spring for GraphQL࣮ͬͯࡍͲ͏ͳͷʁ ʙখنελʔτΞοϓͰͷࣄྫհʙ Crewwגࣜձࣾ ݹլ ༐ࢤ 2025/06/07 JJUG CCC
2025 Spring
ࣗݾհ ॴଐɿCrewwגࣜձࣾ ໊લɿݹլ ༐ࢤ 𝕏 ɿ@yushi_koga Githubɿkogayushi ओઓαʔόαΠυKotlin ΠϯϑϥʢAWSʣ୲
ࠓ͢͜ͱ 1.REST APIʹฐ͕ࣾײ͍ͯͨ͡՝ 2.GraphQLͰͦΕΛͲ͏ղܾͰ͖Δͷ͔ 3.Spring for GraphQLͷ࣮ʹ͍ͭͯ • QueryΛྫʹίϯτϩʔϥʔͷ࣮ํ๏ͷհ •
Subscriptionͷ࣮Ͱ͍͠ͱ͜Ζ
REST APIʹฐ͕ࣾײ͍ͯͨ͡՝
ΫϥΠΞϯταΠυ͔ αʔόʔαΠυ͔ ͲͪΒ͔ʹ࣮ͷෛ୲͕ภΔ REST APIʹฐ͕ࣾײ͍ͯͨ͡՝
ͲΜͳ࡞ΓํΛͯͯ͠՝Λײͨ͡ͷ͔ʁ
େผ͢Δͱ͜ͷ2ͭ 1.Ϧιʔεࢦ 2.Ϣʔεέʔεࢦ ͲΜͳ࡞Γํʁ REST APIΛ࡞Δͱ͖ͷઓུ
ϦιʔεࢦͳREST APIͱʁ ͦͷ՝ʁ
ରϦιʔεࣗͷଐੑͷΈΛฦ͢ɻ ؔ࿈Ϧιʔεฦ͞ͳ͍ɻ ฦͨ͠ͱͯ͠IDͳͲඞཁ࠷খݶɻ ϦιʔεࢦͳREST APIͱʁ
Article ├ id ├ title ├ content └ authorUserId User
├ id └ name ϦιʔεࢦͳREST APIͱʁ GET /articles/{articleId} GET /users/{userId} ˢผ్ɺऔಘ͢Δ ྫ͑ɺهࣄͱͦͷߘऀ͕औΓ͍ͨͱ͖ ˢ"SUJDMFͷଐੑͷΈฦ͢ ɹ"VUIPS 6TFS ؚΊͳ͍
ϦιʔεࢦͳREST APIͷ՝ ΫϥΠΞϯτ͕REST APIΛݺͼग़͢ճ͕૿͑ɺ ΫϥΠΞϯτଆͷ࣮͕૿͑Δ
ϢʔεέʔεࢦͳREST APIͱʁ ͦͷ՝ʁ
ରϦιʔεͱؔ࿈͕͋ΔϦιʔε Ϣʔεέʔε্ඞཁͳΒҰॹʹฦ͢ ϢʔεέʔεࢦͳREST APIͱʁ
Article ├ id ├ title ├ content └ User ├
id └ name GET /articles/{articleId} ϢʔεέʔεࢦͳREST APIͱʁ ˡରϦιʔεͱؔ࿈ͷ͋ΔϦιʔεฦ͢ ྫ͑ɺهࣄͱͦͷߘऀ͕औΓ͍ͨͱ͖
ϢʔεέʔεࢦͳREST APIͷ՝ REST APIαʔόʔʹؔ࿈ϦιʔεΛ·ͱΊͯऔಘͯ͠ฦ͢ ॲཧ͕ඞཁʹͳΓɺαʔόʔαΠυͷ࣮͕૿͑Δ
Ͳ͏ͬͯղܾ͢Δ͔ʁ
Ͳ͏ͬͯղܾ͢Δ͔ ϦιʔεࢦͰ࡞ͬͨͱ͖ͱ ϢʔεέʔεࢦͰ࡞ͬͨͱ͖ͷ ྑ͍ͱ͜औΓ͕Ͱ͖ͨΒ͍͍ͷͰʁ
ͨ·ͨ·ग़ձͬͨSpring for GraphQL ͳΜͯݴޠԽͰ͖ͯͳ͔͚ͬͨͲɺ Spring for GraphQL͕GAʹͳͬͨͷͰɺ খنҊ݅Ͱࢼͯ͠Έͨ
՝ΛղܾͰ͖Δ͔ʂ Spring for GraphQL ϦιʔεࢦͰ࡞ͬͨͱ͖ͱ ϢʔεέʔεࢦͰ࡞ͬͨͱ͖ͷ ྑ͍ͱ͜औΓ͕Ͱ͖ͦ͏ͬͯࢥͬͨ
GraphQLͱԿ͔ ͲͷΑ͏ʹͯ͠՝Λղܾ͢Δͷ͔
GraphQLͱԿ͔ʁ
GraphQLͱԿ͔ʁ GraphQLAPIͷͨΊͷΫΤϦݴޠ ΫΤϦΛ࣮ߦͯ͠σʔλऔಘ͢ΔͨΊͷϥϯλΠϜ
GraphQLͱԿ͔ʁ ΫΤϦݴޠͷଆ໘ͱ ϥϯλΠϜͷଆ໘Λ ͚ͯߟ͑Δ
APIͷͨΊͷΫΤϦݴޠʁ ʢԿ͕ݴ͍͍ͨͷ͔Α͘Θ͔Βͳ͍🤔❓ʣ
ΫΤϦݴޠʹ༷ͱಡΈସ͑Α͏
GraphQLͷ༷ͱʁ GraphQLͲͷΑ͏ͳάϥϑߏ͕ଘࡏ͢Δ͔ͱɺ ͦͷάϥϑʹͲͷΑ͏ͳૢ࡞͕ ڐՄ͞Ε͍ͯΔ͔Λఆٛ͢ΔͨΊͷ༷ ͦͷ༷ͷදݱखஈ͕εΩʔϚఆٛ
͡Ό͋ɺϥϯλΠϜʁ
GraphQLͷϥϯλΠϜͱʁ ࣮ߦڥʹSpring for GraphQL
GraphQLͱSpring for GraphQLͱ ࢲୡ͕࣮͢ΔՕॴͷؔੑΛՄࢹԽͯ͠ΈΔ
GraphQLͱSpring for GraphQLͱ ࢲୡ͕࣮͢ΔՕॴͷؔੑΛՄࢹԽͯ͠ΈΔ
ίϯτϩʔϥʔ ίϯτϩʔϥʔ ίϯτϩʔϥʔ ΤϯςΟςΟ ΤϯςΟςΟ ΤϯςΟςΟ GraphQLͱSpring for GraphQLͱ ࢲୡ͕࣮͢ΔՕॴͷؔੑΛՄࢹԽͯ͠ΈΔ
GraphQLREST APIͷ՝Λ Ͳ͏ղܾͰ͖Δͷ͔
REST APIʹฐ͕ࣾײ͍ͯͨ͡՝ Ϧιʔεࢦ ΫϥΠΞϯτ͕REST APIΛݺͼग़͢ճ͕૿͑ɺ ΫϥΠΞϯτଆͷ࣮͕૿͑Δ Ϣʔεέʔεࢦ REST APIαʔόʔʹؔ࿈ϦιʔεΛ·ͱΊͯऔಘͯ͠ฦ͢ ॲཧ͕ඞཁʹͳΓɺαʔόʔαΠυͷ࣮͕૿͑Δ
ίϯτϩʔϥʔ ίϯτϩʔϥʔ ίϯτϩʔϥʔ ΤϯςΟςΟ ΤϯςΟςΟ ΤϯςΟςΟ ՝ΛͲ͏ղܾͰ͖Δͷ͔ هࣄͷλΠτϧͱͦͷߘ໊Λऔಘ͢Δ
ίϯτϩʔϥʔ ίϯτϩʔϥʔ ίϯτϩʔϥʔ ΤϯςΟςΟ ΤϯςΟςΟ ΤϯςΟςΟ ՝ΛͲ͏ղܾͰ͖Δͷ͔ هࣄͷλΠτϧͱͦͷߘ໊Λऔಘ͢Δ
ίϯτϩʔϥʔ ίϯτϩʔϥʔ ίϯτϩʔϥʔ ΤϯςΟςΟ ΤϯςΟςΟ ΤϯςΟςΟ ՝ΛͲ͏ղܾͰ͖Δͷ͔ هࣄͷλΠτϧͱͦͷߘ໊Λऔಘ͢Δ
ίϯτϩʔϥʔ ίϯτϩʔϥʔ ίϯτϩʔϥʔ ΤϯςΟςΟ ΤϯςΟςΟ ΤϯςΟςΟ ՝ΛͲ͏ղܾͰ͖Δͷ͔ هࣄͷλΠτϧͱͦͷߘ໊Λऔಘ͢Δ
ίϯτϩʔϥʔ ίϯτϩʔϥʔ ίϯτϩʔϥʔ ΤϯςΟςΟ ΤϯςΟςΟ ΤϯςΟςΟ ՝ΛͲ͏ղܾͰ͖Δͷ͔ هࣄͷλΠτϧͱίϝϯτͱͦͷߘऀ໊Λऔಘ͢Δ
ίϯτϩʔϥʔ ίϯτϩʔϥʔ ίϯτϩʔϥʔ ΤϯςΟςΟ ΤϯςΟςΟ ΤϯςΟςΟ ՝ΛͲ͏ղܾͰ͖Δͷ͔ هࣄͷλΠτϧͱίϝϯτͱͦͷߘऀ໊Λऔಘ͢Δ
ίϯτϩʔϥʔ ίϯτϩʔϥʔ ίϯτϩʔϥʔ ΤϯςΟςΟ ΤϯςΟςΟ ΤϯςΟςΟ ՝ΛͲ͏ղܾͰ͖Δͷ͔ هࣄͷλΠτϧͱίϝϯτͱͦͷߘऀ໊Λऔಘ͢Δ
ίϯτϩʔϥʔ ίϯτϩʔϥʔ ίϯτϩʔϥʔ ΤϯςΟςΟ ΤϯςΟςΟ ΤϯςΟςΟ ՝ΛͲ͏ղܾͰ͖Δͷ͔ هࣄͷλΠτϧͱίϝϯτͱͦͷߘऀ໊Λऔಘ͢Δ
ίϯτϩʔϥʔ ίϯτϩʔϥʔ ίϯτϩʔϥʔ ΤϯςΟςΟ ΤϯςΟςΟ ΤϯςΟςΟ ՝ΛͲ͏ղܾͰ͖Δͷ͔ هࣄͷλΠτϧͱίϝϯτͱͦͷߘऀ໊Λऔಘ͢Δ
query articlesWithComments { articles { title comments { content author
{ name } } } } 1.৽ணهࣄʢλΠτϧʣͱߘͨ͠Ϣʔβʔ໊͕΄͍͠ 2.هࣄͷλΠτϧҰཡͱɺ͍ͭͨίϝϯτͱͦͷߘऀΓ͍ͨ ϢʔεέʔεʹԠͨ͡ΫΤϦΛॻ͚ͩ͘ query fetchNewBlogForTopPage { articles { title author { name } } }
query articlesWithComments { articles { title comments { content author
{ name } } } } 1.৽ணهࣄʢλΠτϧʣͱߘͨ͠Ϣʔβʔ໊͕΄͍͠ 2.هࣄͷλΠτϧҰཡͱɺ͍ͭͨίϝϯτͱͦͷߘऀΓ͍ͨ ϢʔεέʔεʹԠͨ͡ΫΤϦΛॻ͚ͩ͘ query fetchNewBlogForTopPage { articles { title author { name } } } ˡهࣄʢλΠτϧʣͷҰཡ
query articlesWithComments { articles { title comments { content author
{ name } } } } 1.৽ணهࣄʢλΠτϧʣͱߘͨ͠Ϣʔβʔ໊͕΄͍͠ 2.هࣄͷλΠτϧҰཡͱɺ͍ͭͨίϝϯτͱͦͷߘऀΓ͍ͨ ϢʔεέʔεʹԠͨ͡ΫΤϦΛॻ͚ͩ͘ query fetchNewBlogForTopPage { articles { title author { name } } } ˡهࣄʢλΠτϧʣͷҰཡ ˡߘͨ͠Ϣʔβʔ໊
query articlesWithComments { articles { title comments { content author
{ name } } } } 1.৽ணهࣄʢλΠτϧʣͱߘͨ͠Ϣʔβʔ໊͕΄͍͠ 2.هࣄͷλΠτϧҰཡͱɺ͍ͭͨίϝϯτͱͦͷߘऀΓ͍ͨ ϢʔεέʔεʹԠͨ͡ΫΤϦΛॻ͚ͩ͘ query fetchNewBlogForTopPage { articles { title author { name } } } ˡهࣄͷλΠτϧͷҰཡ
query articlesWithComments { articles { title comments { content author
{ name } } } } 1.৽ணهࣄʢλΠτϧʣͱߘͨ͠Ϣʔβʔ໊͕΄͍͠ 2.هࣄͷλΠτϧҰཡͱɺ͍ͭͨίϝϯτͱͦͷߘऀΓ͍ͨ ϢʔεέʔεʹԠͨ͡ΫΤϦΛॻ͚ͩ͘ query fetchNewBlogForTopPage { articles { title author { name } } } ˡهࣄͷλΠτϧͷҰཡ ˡ͍ͭͨίϝϯτ
query articlesWithComments { articles { title comments { content author
{ name } } } } 1.৽ணهࣄʢλΠτϧʣͱߘͨ͠Ϣʔβʔ໊͕΄͍͠ 2.هࣄͷλΠτϧҰཡͱɺ͍ͭͨίϝϯτͱͦͷߘऀΓ͍ͨ ϢʔεέʔεʹԠͨ͡ΫΤϦΛॻ͚ͩ͘ query fetchNewBlogForTopPage { articles { title author { name } } } ˡهࣄͷλΠτϧͷҰཡ ˡ͍ͭͨίϝϯτ ˡίϝϯτͷߘऀ
Ͳ͏ղܾ͔ͨ͠ Ϧιʔεࢦ ΫϥΠΞϯτ͕REST APIΛݺͼग़͢ճ͕૿͑ɺ ΫϥΠΞϯτଆͷ࣮͕૿͑Δ Ϣʔεέʔεࢦ REST APIαʔόʔʹؔ࿈ϦιʔεΛ·ͱΊͯऔಘͯ͠ฦ͢ॲ ཧ͕ඞཁʹͳΓɺαʔόʔαΠυͷ࣮͕૿͑Δ
Ͳ͏ղܾ͔ͨ͠ Ϧιʔεࢦ ΫϥΠΞϯτ͕REST APIΛݺͼग़͢ճ͕૿͑ɺ ΫϥΠΞϯτଆͷ࣮͕૿͑Δ ɹˠΫΤϦΛҰճݺͼग़͚ͩ͢Ͱ͢Μͩ Ϣʔεέʔεࢦ REST APIαʔόʔʹؔ࿈ϦιʔεΛ·ͱΊͯऔಘͯ͠ฦ͢ॲ ཧ͕ඞཁʹͳΓɺαʔόʔαΠυͷ࣮͕૿͑Δ
ίϯτϩʔϥʔ ίϯτϩʔϥʔ ίϯτϩʔϥʔ ΤϯςΟςΟ ΤϯςΟςΟ ΤϯςΟςΟ ͍ઢͱਤͷͱ͜ΖΛҰ࣮͢Ε ϢʔεέʔεͷόϦΤʔγϣϯʢΫΤϦʣʹԠͨ͡ ݸผͷ࣮͕͍Βͳ͍
Ͳ͏ղܾ͔ͨ͠ Ϧιʔεࢦ ΫϥΠΞϯτ͕REST APIΛݺͼग़͢ճ͕૿͑ɺ ΫϥΠΞϯτଆͷ࣮͕૿͑Δ ɹˠΫΤϦΛҰճݺͼग़͚ͩ͢Ͱ͢Μͩ Ϣʔεέʔεࢦ REST APIαʔόʔʹؔ࿈ϦιʔεΛ·ͱΊͯऔಘͯ͠ฦ͢ॲ ཧ͕ඞཁʹͳΓɺαʔόʔαΠυͷ࣮͕૿͑Δ
→ΫΤϦΛΤοδΛ࣮͢ΕɺͦΕҎ্ͷෆཁ
Spring for GraphQLͷ࣮ํ๏
εΩʔϚͷॻ͖ํ UZQF2VFSZ\ BSUJDMFT<"SUJDMF> BSUJDMF BSUJDMF*E*% "SUJDMF ^ UZQF.VUBUJPO\ QPTU"SUJDMF JOQVU"SUJDMF*OQVU
"SUJDMF ^ UZQF4VCTDSJQUJPO\ VQEBUFE"SUJDMFT BSUJDMF*ET<*%> "SUJDMF ^ JOQVU"SUJDMF*OQVU\ DPOUFOU4USJOH UJUMF4USJOH ^ UZQF"SUJDMF\ BVUIPS6TFS DPNNFOUT<$PNNFOU> DPOUFOU4USJOH JE*% MJLFE#Z<6TFS> UJUMF4USJOH ^
UZQF2VFSZ\ BSUJDMFT<"SUJDMF> BSUJDMF BSUJDMF*E*% "SUJDMF ^ UZQF.VUBUJPO\ QPTU"SUJDMF JOQVU"SUJDMF*OQVU "SUJDMF
^ UZQF4VCTDSJQUJPO\ VQEBUFE"SUJDMFT BSUJDMF*ET<*%> "SUJDMF ^ JOQVU"SUJDMF*OQVU\ DPOUFOU4USJOH UJUMF4USJOH ^ UZQF"SUJDMF\ BVUIPS6TFS DPNNFOUT<$PNNFOU> DPOUFOU4USJOH JE*% MJLFE#Z<6TFS> UJUMF4USJOH ^ ˡJOQVUೖྗ εΩʔϚͷॻ͖ํ
UZQF2VFSZ\ BSUJDMFT<"SUJDMF> BSUJDMF BSUJDMF*E*% "SUJDMF ^ UZQF.VUBUJPO\ QPTU"SUJDMF JOQVU"SUJDMF*OQVU "SUJDMF
^ UZQF4VCTDSJQUJPO\ VQEBUFE"SUJDMFT BSUJDMF*ET<*%> "SUJDMF ^ JOQVU"SUJDMF*OQVU\ DPOUFOU4USJOH UJUMF4USJOH ^ UZQF"SUJDMF\ BVUIPS6TFS DPNNFOUT<$PNNFOU> DPOUFOU4USJOH JE*% MJLFE#Z<6TFS> UJUMF4USJOH ^ ˡJOQVUೖྗ ˡzzඞਢ εΩʔϚͷॻ͖ํ
UZQF2VFSZ\ BSUJDMFT<"SUJDMF> BSUJDMF BSUJDMF*E*% "SUJDMF ^ UZQF.VUBUJPO\ QPTU"SUJDMF JOQVU"SUJDMF*OQVU "SUJDMF
^ UZQF4VCTDSJQUJPO\ VQEBUFE"SUJDMFT BSUJDMF*ET<*%> "SUJDMF ^ JOQVU"SUJDMF*OQVU\ DPOUFOU4USJOH UJUMF4USJOH ^ UZQF"SUJDMF\ BVUIPS6TFS DPNNFOUT<$PNNFOU> DPOUFOU4USJOH JE*% MJLFE#Z<6TFS> UJUMF4USJOH ^ ˡUZQFग़ྗ εΩʔϚͷॻ͖ํ
UZQF2VFSZ\ BSUJDMFT<"SUJDMF> BSUJDMF BSUJDMF*E*% "SUJDMF ^ UZQF.VUBUJPO\ QPTU"SUJDMF JOQVU"SUJDMF*OQVU "SUJDMF
^ UZQF4VCTDSJQUJPO\ VQEBUFE"SUJDMFT BSUJDMF*ET<*%> "SUJDMF ^ JOQVU"SUJDMF*OQVU\ DPOUFOU4USJOH UJUMF4USJOH ^ UZQF"SUJDMF\ BVUIPS6TFS DPNNFOUT<$PNNFOU> DPOUFOU4USJOH JE*% MJLFE#Z<6TFS> UJUMF4USJOH ^ ˡUZQFग़ྗ ˢz<>zྻ εΩʔϚͷॻ͖ํ
UZQF2VFSZ\ BSUJDMFT<"SUJDMF> BSUJDMF BSUJDMF*E*% "SUJDMF ^ UZQF.VUBUJPO\ QPTU"SUJDMF JOQVU"SUJDMF*OQVU "SUJDMF
^ UZQF4VCTDSJQUJPO\ VQEBUFE"SUJDMFT BSUJDMF*ET<*%> "SUJDMF ^ JOQVU"SUJDMF*OQVU\ DPOUFOU4USJOH UJUMF4USJOH ^ UZQF"SUJDMF\ BVUIPS6TFS DPNNFOUT<$PNNFOU> DPOUFOU4USJOH JE*% MJLFE#Z<6TFS> UJUMF4USJOH ^ ˡUZQFग़ྗ ˢz<>zྻ ˡzz/PU/VMM εΩʔϚͷॻ͖ํ
UZQF2VFSZ\ BSUJDMFT<"SUJDMF> BSUJDMF BSUJDMF*E*% "SUJDMF ^ UZQF.VUBUJPO\ QPTU"SUJDMF JOQVU"SUJDMF*OQVU "SUJDMF
^ UZQF4VCTDSJQUJPO\ VQEBUFE"SUJDMFT BSUJDMF*ET<*%> "SUJDMF ^ JOQVU"SUJDMF*OQVU\ DPOUFOU4USJOH UJUMF4USJOH ^ UZQF"SUJDMF\ BVUIPS6TFS DPNNFOUT<$PNNFOU> DPOUFOU4USJOH JE*% MJLFE#Z<6TFS> UJUMF4USJOH ^ ˣUZQF2VFSZͱͯ͠ఆٛ εΩʔϚͷॻ͖ํ
UZQF2VFSZ\ BSUJDMFT<"SUJDMF> BSUJDMF BSUJDMF*E*% "SUJDMF ^ UZQF.VUBUJPO\ QPTU"SUJDMF JOQVU"SUJDMF*OQVU "SUJDMF
^ UZQF4VCTDSJQUJPO\ VQEBUFE"SUJDMFT BSUJDMF*ET<*%> "SUJDMF ^ JOQVU"SUJDMF*OQVU\ DPOUFOU4USJOH UJUMF4USJOH ^ UZQF"SUJDMF\ BVUIPS6TFS DPNNFOUT<$PNNFOU> DPOUFOU4USJOH JE*% MJLFE#Z<6TFS> UJUMF4USJOH ^ ˣUZQF2VFSZͱͯ͠ఆٛ ˡҾΛࢦఆՄ εΩʔϚͷॻ͖ํ
αϯϓϧίʔυͱͦͷٕज़ελοΫ ಈ͘αϯϓϧίʔυ • https://github.com/kogayushi/spring-for-graphql-tips-by-small-startup ݴޠɿ Kotlin 2.0.21 ϑϨʔϜϫʔΫɿ Spring Boot
3.4.4 • Spring for GraphQL • Spring MVC
Query࣮ྫ
type Query { article( articleId: ID! ): Article } type
Article { id: ID! title: String! content: String! author: User! comments: [Comment!]! } @Controller class ArticleGraphQLController( private val fetchArticles: FetchArticles, ) { @QueryMapping fun article( @Argument articleId : UUID ): Article? { val articles = fetchArticleByArticleId.handle(articleId) return article?.toArticleDto() } } data class Article( val id: UUID, val title: String, val content: String, val authorId: UUID, ) Query࣮ྫɿQueryͷϚοϐϯάϧʔϧ
type Query { article( articleId: ID! ): Article } type
Article { id: ID! title: String! content: String! author: User! comments: [Comment!]! } @Controller class ArticleGraphQLController( private val fetchArticles: FetchArticles, ) { @QueryMapping fun article( @Argument articleId : UUID ): Article? { val articles = fetchArticleByArticleId.handle(articleId) return article?.toArticleDto() } } data class Article( val id: UUID, val title: String, val content: String, val authorId: UUID, ) Query࣮ྫɿQueryͷϚοϐϯάϧʔϧ ˢ!2VFSZ.BQQJOH͕͍͍ͭͯΔ ಉ໊ͷϝιουʹϚοϐϯά
@Controller class ArticleGraphQLController( private val fetchArticles: FetchArticles, ) { @QueryMapping
fun article( @Argument articleId : UUID ): Article? { val articles = fetchArticleByArticleId.handle(articleId) return article?.toArticleDto() } } data class Article( val id: UUID, val title: String, val content: String, val authorId: UUID, ) type Query { article( articleId: ID! ): Article } type Article { id: ID! title: String! content: String! author: User! comments: [Comment!]! } Query࣮ྫɿҾͷϚοϐϯάϧʔϧ ˢ!"SHVNFOU͕͍͍ͭͯΔ ɹಉ໊ͷҾʹϚοϐϯά
@Controller class ArticleGraphQLController( private val fetchArticles: FetchArticles, ) { @QueryMapping
fun article( @Argument articleId : UUID ): Article? { val articles = fetchArticleByArticleId.handle(articleId) return article?.toArticleDto() } } data class Article( val id: UUID, val title: String, val content: String, val authorId: UUID, ) type Query { article( articleId: ID! ): Article } type Article { id: ID! title: String! content: String! author: User! comments: [Comment!]! } Query࣮ྫɿฦΓͷϚοϐϯάϧʔϧ ฦΓͷܕఆٛ
@Controller class ArticleGraphQLController( private val fetchArticles: FetchArticles, ) { @QueryMapping
fun article( @Argument articleId : UUID ): Article? { val articles = fetchArticleByArticleId.handle(articleId) return article?.toArticleDto() } } data class Article( val id: UUID, val title: String, val content: String, val authorId: UUID, ) type Query { article( articleId: ID! ): Article } type Article { id: ID! title: String! content: String! author: User! comments: [Comment!]! } Query࣮ྫɿฦΓͷϚοϐϯάϧʔϧ εΩʔϚͰఆٛͨ͠ϑΟʔϧυ໊ͱ %50ಉ໊ͷϓϩύςΟ͕Ϛοϐϯά͞ΕΔ
@Controller class ArticleGraphQLController( private val fetchArticles: FetchArticles, ) { @QueryMapping
fun article( @Argument articleId : UUID ): Article? { val articles = fetchArticleByArticleId.handle(articleId) return article?.toArticleDto() } } data class Article( val id: UUID, val title: String, val content: String, val authorId: UUID, ) type Query { article( articleId: ID! ): Article } type Article { id: ID! title: String! content: String! author: User! comments: [Comment!]! } Query࣮ྫɿฦΓͷϚοϐϯάϧʔϧ ˢϚοϐϯά͞Εͯͳ͍ɺ͜ͷϑΟʔϧυ&EHF
Edge࣮ྫ
Edge࣮ྫɿEdgeͷϚοϐϯάϧʔϧ @Controller class CommentGraphQLController( private val fetchComments: FetchComments, ) {
@BatchMapping(field = "comments") fun commentsOfArticle(articles: List<Article>): Map<Article, List<Comment>> { val articleIds = articles.map { it.id } val comments = fetchComments.handle( FetchCommentsInputData(articleIds) ) return articles.associateWith { val articleComments = comments .filter { comment -> comment.articleId == it.id } articleComments.map { it.toCommentDto() } } } } type Article { id: ID! title: String! content: String! author: User! comments: [Comment!]! } type User { id: ID! name: String! articles: [Article!]! comments: [Comment!]! }
Edge࣮ྫɿEdgeͷϚοϐϯάϧʔϧ @Controller class CommentGraphQLController( private val fetchComments: FetchComments, ) {
@BatchMapping(field = "comments") fun commentsOfArticle(articles: List<Article>): Map<Article, List<Comment>> { val articleIds = articles.map { it.id } val comments = fetchComments.handle( FetchCommentsInputData(articleIds) ) return articles.associateWith { val articleComments = comments .filter { comment -> comment.articleId == it.id } articleComments.map { it.toCommentDto() } } } } type Article { id: ID! title: String! content: String! author: User! comments: [Comment!]! } type User { id: ID! name: String! articles: [Article!]! comments: [Comment!]! }
Edge࣮ྫɿEdgeͷϚοϐϯάϧʔϧ @Controller class CommentGraphQLController( private val fetchComments: FetchComments, ) {
@BatchMapping(field = "comments") fun commentsOfArticle(articles: List<Article>): Map<Article, List<Comment>> { val articleIds = articles.map { it.id } val comments = fetchComments.handle( FetchCommentsInputData(articleIds) ) return articles.associateWith { val articleComments = comments .filter { comment -> comment.articleId == it.id } articleComments.map { it.toCommentDto() } } } } type Article { id: ID! title: String! content: String! author: User! comments: [Comment!]! } type User { id: ID! name: String! articles: [Article!]! comments: [Comment!]! } ɹɹɹɹɹɹɹɹɹˢ ΤοδͷϑΟʔϧυ໊ͱ Ξϊςʔγϣϯʹࢦఆͨ͠ϑΟʔϧυ໊ͱ Ҿͷํ͕Ұக͢ΔϝιουʹϚοϐϯά
Edge࣮ྫɿEdgeͷϚοϐϯάϧʔϧ @Controller class CommentGraphQLController( private val fetchComments: FetchComments, ) {
@BatchMapping(field = "comments") fun commentsOfArticle(articles: List<Article>): Map<Article, List<Comment>> { val articleIds = articles.map { it.id } val comments = fetchComments.handle( FetchCommentsInputData(articleIds) ) return articles.associateWith { val articleComments = comments .filter { comment -> comment.articleId == it.id } articleComments.map { it.toCommentDto() } } } } type Article { id: ID! title: String! content: String! author: User! comments: [Comment!]! } ϝιουͷҾʹɺؔ࿈ݩϊʔυͷ-JTU͕Θͨͬͯ͘Δˢ
Edge࣮ྫɿEdgeͷϚοϐϯάϧʔϧ @Controller class CommentGraphQLController( private val fetchComments: FetchComments, ) {
@BatchMapping(field = "comments") fun commentsOfArticle(articles: List<Article>): Map<Article, List<Comment>> { val articleIds = articles.map { it.id } val comments = fetchComments.handle( FetchCommentsInputData(articleIds) ) return articles.associateWith { val articleComments = comments .filter { comment -> comment.articleId == it.id } articleComments.map { it.toCommentDto() } } } } type Article { id: ID! title: String! content: String! author: User! comments: [Comment!]! } ίϝϯτΛऔಘͯ͠ɺ औಘϊʔυͱಥ͖߹Θͤͯˠ .BQʹͨ͠ͷΛฦ͢
Subscriptionͷ࣮Ͱ͍͠ͱ͜Ζ
Subscriptionͷ࣮Ͱ͍͠ͱ͜Ζ ReactorͷFluxΛ͏લఏʹͳ͍ͬͯΔ
FluxΛ͏લఏͩͱͳ͍ͥ͠ͷ͔ʁ • Spring Web fl uxͰͳ͘Spring MVCͰಈ͔ͤ͢Δͷͷ…
FluxΛ͏લఏͩͱͳ͍ͥ͠ͷ͔ʁ • Spring Web fl uxͰͳ͘Spring MVCͰಈ͔ͤ͢Δͷͷ… • MVCɿखଓܕʢ໋ྩܕʣϓϩάϥϛϯά •
FluxɿReactiveʢԠܕʣϓϩάϥϛϯά
FluxΛ͏લఏͩͱͳ͍ͥ͠ͷ͔ʁ • Spring Web fl uxͰͳ͘Spring MVCͰಈ͔ͤ͢Δͷͷ… • MVCɿखଓܕʢ໋ྩܕʣϓϩάϥϛϯά •
FluxɿReactiveʢԠܕʣϓϩάϥϛϯά ύϥμΠϜ͕ҧ͏
FluxΛ͏લఏͩͱͳ͍ͥ͠ͷ͔ʁ • Spring Web fl uxͰͳ͘Spring MVCͰಈ͔ͤ͢Δͷͷ… • MVCɿखଓܕʢ໋ྩܕʣϓϩάϥϛϯά •
FluxɿReactiveʢԠܕʣϓϩάϥϛϯά ύϥμΠϜͷؒͷհ͕ඞཁ
FluxΛ͏લఏͩͱͳ͍ͥ͠ͷ͔ʁ • Spring Web fl uxͰͳ͘Spring MVCͰಈ͔ͤ͢Δͷͷ… • MVCɿखଓܕʢ໋ྩܕʣϓϩάϥϛϯά •
FluxɿReactiveʢԠܕʣϓϩάϥϛϯά ύϥμΠϜͷؒͷհ͕ඞཁ ReactorͷSinks
FluxΛ͏લఏͩͱͳ͍ͥ͠ͷ͔ʁ • Spring Web fl uxͰͳ͘Spring MVCͰಈ͔ͤ͢Δͷͷ… • FluxͱSinksͷษڧ͕ඞཁ
FluxΛ͏લఏͩͱͳ͍ͥ͠ͷ͔ʁ • Spring Web fl uxͰͳ͘Spring MVCͰಈ͔ͤ͢Δͷͷ… • FluxͱSinksͷษڧ͕ඞཁ •
Sinksͷ͍ํ͕͍͠
FluxΛ͏લఏͩͱͳ͍ͥ͠ͷ͔ʁ • Spring Web fl uxͰͳ͘Spring MVCͰಈ͔ͤ͢Δͷͷ… • FluxͱSinksͷษڧ͕ඞཁ •
Sinksͷ͍ํ͕͍͠ • ಉ࣌ʹemitΤϥʔ → εϨουηʔϑతͳ͕ඞཁ͔ʁ • BlockingQueueΛͬͯɺඇಉظॲཧΛಉظॲཧʹมߋ • ϫʔΧʔεϨουͰQueue͔ΒऔΓग़ͯ̍݅ͣͭ͠emit • ಈ͘αϯϓϧίʔυʹ࣮ྫΛ༻ҙͨ͠ • https://github.com/kogayushi/spring-for-graphql-tips-by-small-startup
՝ղܾͰ͖ͨͷ͔ʁ Ϧιʔεࢦͷ՝ ΫϥΠΞϯτ͕REST APIΛݺͼग़͢ճ͕૿͑ɺ ΫϥΠΞϯτଆͷ࣮͕૿͑Δ ɹˠ ҰճͷΫΤϦݺͼग़͠ͰඞཁͳσʔλΛऔಘɿ🆗 Ϣʔεέʔεࢦͷ՝ REST APIαʔόʔʹؔ࿈ϦιʔεΛ·ͱΊͯऔಘͯ͠ฦ͢ॲཧ
͕ඞཁʹͳΓɺαʔόʔαΠυͷ࣮͕૿͑Δ ɹˠΫΤϦͱΤοδͷ࣮͢Δ͚ͩͰࡁΉɿ🆗
ϝϦοτʁ • ϑϩϯτΤϯυɺόοΫΤϯυͱʹ։ൃޮ্͕🆙 • ϑϩϯτΤϯυσʔλಥ͖߹Θͤॲཧ͕ͳ͘ͳ࣮ͬͯྔ͕ݮͬͨ • όοΫΤϯυ࣮ର͕ݮΓɺ։ൃྔΛେ͖͘ݮΒͤͨ
σϝϦοτʁ • Subscriptionͷ࣮қ͕ߴ͍🙄 • ͜ͷ͋ͱհ͢Δɺಈ͘αϯϓϧίʔυ͕ࢀߟʹͳΔ͔ʁ • ϝϯόʔͷܦݧ͕ઙ͍͏ͪɺEdgeͷݺͼग़͠ͰN+1සൃ͕ͪ͠🐢 • BatchMappingͷར༻ΛపఈΛపఈ͢Ε͋Δఔղܾ͢Δ •
ϑΝΠϧΞοϓϩʔυํ๏ͷਖ਼ղ͕ʢࠓͷͱ͜ΖʣΘ͔Βͳ͍🤷 • GraphQLʹϑΝΠϧΞοϓϩʔυͷ༷͕ͳ͍ • ैདྷͷΓํͰΔ͔͠ͳ͍ʁࡧத • εΩʔϚఆٛͷߋ৽࣌ɺࠩΛ͑Δํ๏ʹΉ • ·͍͍ͩΓํ͕ࢥ͍͍ͭͯͳ͍ɺࡧத • ࠓslackͰڞ༗ɺਓྗରॲ💪
ϦϑΝϨϯε • ϒϩάهࣄ 1.ංେԽ͢ΔεΩʔϚఆٛͷରॲํ๏ https://qiita.com/yushi_koga/items/f05a7b23b20e61aa1269 2.෦ߋ৽͍ͨ͠ https://qiita.com/yushi_koga/items/f749dd53e32fafb23eee 3.MutationͷΓͷํΛVoidʹ͢Δํ๏ https://qiita.com/yushi_koga/items/45b87eff5b40a5ec86f0 4.ࢄτϨʔγϯάͰGraphQLͷ۠ผΛ͚ͭΔํ๏
https://qiita.com/yushi_koga/items/8051f98e093dbbe8f51d • ಈ͘αϯϓϧίʔυ https://github.com/kogayushi/spring-for-graphql-tips-by-small-startup
Spring for GraphQL࣮ͬͯࡍͲ͏ͳͷʁ ʙখنελʔτΞοϓͰͷࣄྫհʙ Crewwגࣜձࣾ ݹլ ༐ࢤ 2025/06/07 JJUG CCC
2025 Spring