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
2018.4.18 - GraphQLが格好良さそうだったので、Springで入門したら躓いた話
Search
hainet50b
April 18, 2018
Programming
1.3k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
2018.4.18 - GraphQLが格好良さそうだったので、Springで入門したら躓いた話
▼開催日▼
2018年4月18日
▼イベント▼
JSUG勉強会 2018年 その3
https://jsug.doorkeeper.jp/events/73144
hainet50b
April 18, 2018
More Decks by hainet50b
See All by hainet50b
2023.6.23 - AWS Dev Day 2023 Tokyo Spring Boot 3.0 クラウドネイティブ関連機能 & JSUG(日本Springユーザ会)活動紹介
hainet50b
0
260
2023.2.17 - Spring Boot 3.0 オブザーバビリティツアーガイド
hainet50b
3
1.7k
2021.12.3 - 決済システムで学ぶレジリエントなサービスのいろは
hainet50b
13
4.9k
2019.10.4 - 決済トランザクションの監視におけるElastic Stackの活用 ~アプリケーションロギング/トレーシングの可視化~
hainet50b
5
2.7k
Other Decks in Programming
See All in Programming
re:Inventに行く前に知っておきたい現地参加のノウハウ
nokomoro3
0
390
FreeBSDでZabbixを動かす
kenkino
0
360
見えないものを探る要求要件定義に必要な基本的思考 / invisible-requirement-thinking
minodriven
15
8k
難しいけど、読めた。- OSSの入口に立った話。
sts11142
0
140
IBM Bob Dojo #1 仕様駆動開発入門
oniak3ibm
PRO
0
360
Starting & Sustaining Code-Based E2E Testing for Non-Coding QA Teams( #jasstniigata )
teyamagu
PRO
1
940
[ハンズオン]AIへの指示だけで「五目並べ」を作ってみよう
satoshi256kbyte
1
360
個人開発者よ、Honoを使え(おじさんver)
tanahiro2010
2
140
ソニーのクラウド共通基盤の変遷とAI時代の開発スタイルに合わせた進化 / The Journey of Sony’s Common Cloud Platform and Its Evolution for AI-Native Development
kenjiyoneyama
0
240
更なる可用性を求めて、5年間運用したKotlinのアプリケーションをGoでリプレイスする話
ken_tunc
0
450
iOS 27でニュースアプリはどう変わる!? 〜日経電子版の新機能対応と、開発事例から〜
lynnswap
7
13k
モデルのリファクタリングが難しいと思ったら、そもそも複雑だったのはビジネス仕様だった ? / is-the-business-domain-the-real-complexity
hatsu38
0
450
Featured
See All Featured
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
460
Building a Modern Day E-commerce SEO Strategy
aleyda
45
9.2k
AI in Enterprises - Java and Open Source to the Rescue
ivargrimstad
0
1.5k
The agentic SEO stack - context over prompts
schlessera
0
970
[Rails World 2026] Durable orchestration on Rails: from continuation to workflow
palkan
1
510
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
370
Self-Hosted WebAssembly Runtime for Runtime-Neutral Checkpoint/Restore in Edge–Cloud Continuum
chikuwait
0
870
Ten Tips & Tricks for a 🌱 transition
stuffmc
1
260
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
203
77k
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
We Analyzed 250 Million AI Search Results: Here's What I Found
joshbly
2
2k
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
270
Transcript
GraphQL͕ ֨ྑͦ͞͏ͩͬͨͷͰɺ SpringͰೖͨ͠Β͍᪴ͨ
About me • ∁ ͍Ͷ • SoftBank Payment Service Corp.
• ϓϩάϥϚʔ3ੜ ։ൃϓϩδΣΫτࢧԉ • I — Spring Framework! hainet hainet_g
None
“(GraphQL is) A query language for your API” ཉ͍͠ͷ͚ͩऔಘ͢Δ WebAPiͷΈ
ྫɿΫϨδοτΧʔυใΛऔಘ͢Δ
Χʔυ൪߸(number)Λऔಘ͢Δ
༗ޮظݶ(goodThru)औಘ͢Δ
ϒϥϯυ(brand)औಘ͢Δ ཁٻ͞ΕΔ·ͰόοΫΤϯυͰ ϒϥϯυΛऔಘ͢ΔΫΤϦಈ͔ͳ͍
3 Slides Cooking! with GraphQL Spring Boot Starter
1. Add Dependencies dependencies { compile('com.graphql-java:graphql-spring-boot-starter:4.0.0') compile(‘com.graphql-java:graphql-java-tools:4.3.0') }
2. Describe Schemas type CreditCard { id: ID! number: String!
goodThru: String! securityCode: String! } type Query { findCreditCards: [CreditCard]! }
3. Implement Queries @Component public class CreditCardQuery implements GraphQLQueryResolver {
public List<CreditCard> findCreditCards() { // Find credit cards! return creditCards; } }
Run! https://github.com/hainet/ graphql-spring-boot-sample
খ͞ͳϓϩδΣΫτʹಋೖͨ͠ͷͰɺ GraphQLͷ֨ྑ͞Λڞ༗͍ͨ͠ʂ ʢͱࢥͬͯLTͷ४උΛ࢝Ί·ͨ͠ɻʣ
͕ɺࠓ͢Δ GraphQLͷ֨ྑ͍ …Ͱͳ͍ɻ ྑ͍ॴࢁ͋ͬͯհ͍ͨ͠… @ComponentΛ͚ͭΔ͚ͩͰେମԿͰग़དྷΔͱ͔ɺ Schemaͱ࣮͕Ұக͍ͯ͠ͳ͍ͱڭ͑ͯ͘ΕΔͱ͔ɺ ෆཁͳResolverಈ͔ͳ͍(ෆཁͳSQLྲྀΕͳ͍)ͱ͔ɺ LTͳͷͰׂѪ (´ʀωʀʆ)
ͬͯΈ͔ͯͬͨ SpringϢʔβʔ͔ͩΒͦ᪴͘͜ ͱͯຯͳ࣮ͷΛ͠·͢ɻ
࣮Ͱ͍᪴ͨ͜ͱ 1. PayloadΛόϦσʔγϣϯ͢Δ ͖᪴ɿ˒˒˒ˑˑ 2. ΤϥʔϋϯυϦϯά͢Δ ͖᪴ɿ˒˒˒˒˒˒˒˒˒˒ 3. ObjectMapperΛઃఆ͢Δ(Appendix) ͖᪴ɿ˒ˑˑˑˑ
PayloadΛόϦσʔγϣϯ͢Δ ͖᪴ɿ˒˒˒ˑˑ
Hibernate ValidatorΛ͍͍ͨ public class CreditCardPayload { @NotNull @Size(min = 14,
max = 16) @Pattern(regexp = "[0-9]*") private String number; // Fields, Getters, Setters } @Component public class CreditCardMutation implements GraphQLMutationResolver { public CreditCard createCreditCard( @Validated final CreditCardPayload payload) { // Create credit card! return creditCard; } }
Hibernate ValidatorΛ͍͍ͨ public class CreditCardPayload { @NotNull @Size(min = 14,
max = 16) @Pattern(regexp = "[0-9]*") private String number; // Fields, Getters, Setters } @Component public class CreditCardMutation implements GraphQLMutationResolver { public CreditCard createCreditCard( @Validated final CreditCardPayload payload) { // Create credit card! return creditCard; } }
Hibernate ValidatorΛ͍͍ͨ public class CreditCardPayload { @NotNull @Size(min = 14,
max = 16) @Pattern(regexp = "[0-9]*") private String number; // Fields, Getters, Setters } @Component public class CreditCardMutation implements GraphQLMutationResolver { public CreditCard createCreditCard( @Validated final CreditCardPayload payload) { // Create credit card! return creditCard; } }
ͷւɹɹ༏ ΫϥΠΞϯτ αΠυͰʂ UI/UX! ଈ࣌ Ϩεϙϯεʂ ͦ ͏ ͡ Ό
Ͷ ͐ ʂ ͷຽ͕જΔʹաࠅͳڥ
GraphQL Javaެࣜʹ͋ͬͨʂ URL: http://graphql-java.readthedocs.io/en/latest/scalars.html#validation-of-input-and-output java.lang.String จࣈྻ Scalar GraphQLString Deserialize Serialize
Scalarͱ ͜͜Ͱ όϦσʔγϣϯ͢Δ
Number.javaΛ࡞ @Component public class Number extends GraphQLScalarType { public Number()
{ super("Number", "CreditCardPayload.Number", new Coercing() { @Override public String serialize(final Object dataFetcherResult) { return this.serializeNumber(dataFetcherResult); } @Override public String parseValue(final Object input) { return this.parseNumberFromVariable(input); } @Override public String parseLiteral(final Object input) { return this.parseNumberFromAstLiteral(input); } }); } } Serialize Deserialize (AST) Deserialize
input CreditCardPayload { number: Number! goodThru: GoodThru! brand: Brand! securityCode:
SecurityCode! } Number.java CreditCard.graphqlsఆٛมߋ GoodThru.java SecurityCode.java
όϦσʔγϣϯͰ͖ͨʂ DDDతͳఆٛ៉ྷʂ ͚ͩΕͲ...
type Something { foo: Foo! bar: Bar! baz: Baz! hoge:
Hoge! fuga: Fuga! piyo: Piyo! fizz: Fizz! bazz: Bazz! } Foo.java Bar.java Baz.java Hoge.java Fuga.java Piyo.java Fizz.java Bazz.java
ཉ͔ͬͨ͠ͷ ͜Ε͡Όͳ͍
ΤϥʔϋϯυϦϯά͢Δ ͖᪴ɿ˒˒˒˒˒˒˒˒˒˒
Τϥʔ࣌ɺGraphQL Spring Boot Starter ͱΓ͋͑ͣInternal Server ErrorΛฦ͢ HandlerExceptionResolver͢Γൈ͚Δ...
Τϥʔϋϯυϥͷ࣮Λͬͯɺ ॲཧΛࠩ͠ସ͑ͯΈͨ @Override public List<GraphQLError> processErrors( List<GraphQLError> errors) { errors.add(
new GenericGraphQLError( "Internal Server Error(s) while executing query" ) ); // Omitted return errors; } DefaultGraphQLErrorHandler.java
Τϥʔϋϯυϥͷ࣮Λͬͯɺ ॲཧΛࠩ͠ସ͑ͯΈͨ @Override public List<GraphQLError> processErrors( List<GraphQLError> errors) { errors.add(
new GenericGraphQLError( "Internal Server Error(s) while executing query" ) ); // Omitted return errors; } MyGraphQLErrorHandler.java
None
ʊਓਓਓਓਓਓਓਓਓʊ ʼɹ Stack Trace ɹʻ ʉY^Y^Y^Y^Y^Y^Yʉ
Internal Server Error Ͳ͜Ζͷ૽͗Ͱͳ͍
GraphQLErrorΛྨͯ͠… GraphQLErrorେ͖͘3छྨ
GraphQLErrorΛదʹϋϯυϦϯά͢Δ GraphQLErrorHandlerΛ࣮ @Component @Slf4j public class GraphQLErrorHandlerConfig implements GraphQLErrorHandler {
@Override public List<GraphQLError> processErrors(final List<GraphQLError> errors) { errors.stream() .filter(this::isServerError) .forEach(error -> { if (error instanceof Throwable) { log.error("Error executing query!", (Throwable) error); } else { log.error( "Error executing query ({}): {}", error.getClass().getSimpleName(), error.getMessage() ); } }); return errors.stream() .map(GraphQLErrorHolder::new) .collect(Collectors.toList()); } private boolean isServerError(final GraphQLError error) { return error instanceof ExceptionWhileDataFetching || error instanceof Throwable; } }
@Component @Slf4j public class GraphQLErrorHandlerConfig implements GraphQLErrorHandler { @Override public
List<GraphQLError> processErrors(final List<GraphQLError> errors) { errors.stream() .filter(this::isServerError) .forEach(error -> { if (error instanceof Throwable) { log.error("Error executing query!", (Throwable) error); } else { log.error( "Error executing query ({}): {}", error.getClass().getSimpleName(), error.getMessage() ); } }); return errors.stream() .map(GraphQLErrorHolder::new) .collect(Collectors.toList()); } private boolean isServerError(final GraphQLError error) { return error instanceof ExceptionWhileDataFetching || error instanceof Throwable; } } Stack Trace ࣗྗͰग़ྗ GraphQLErrorΛదʹϋϯυϦϯά͢Δ GraphQLErrorHandlerΛ࣮
GraphQLErrorHolderΛ࡞ public class GraphQLErrorHolder implements GraphQLError { private GraphQLError error;
public GraphQLErrorHolder( final GraphQLError error) { this.error = error; } // Override getters } ThrowableΛӅṭ
ʂ ҙͷϝοηʔδ ҙͷΤϥʔλΠϓ ҙͷkey/value
ここまで躓いて 私は考えた
何か⼤きな勘違いを しているのではないか... Hibernate Validatorは利かない, HandlerExceptionResolverはすり抜ける, ObjectMapperは独⾃のインターフェースを通して登録する. 共通点は? 情報が少なすぎて調査が難航。 上司からもJavaでGraphQL誰もやっていないとツッコミを受ける。
/graphqlΤϯυϙΠϯτ Spring MVCͷ֎ͷੈքͷॅਓͩͬͨ ʲग़དྷͳ͍͜ͱʳ ɾHandlerInterceptor ɾetc… GraphQLWebAutoConfiguration.java ʲग़དྷΔ͜ͱʳ ɾSpring AOP
ɾFilter ɹˠ Spring SecurityOK!
Springͷੈք͔͠Βͳ͔ͬͨͷͰ େ͖͘ζοίέ·ͨ͠... ɹɹɹɹɹɹɹ㱯ɹ㱯 ɹɹɹɹɹʢŋТŋccɹɹζίʔ ɹɹɹɹɹɹɹcɹɹc ɹɹɹɹɹ㱬㱬@@@@ϊɹʹj ʢϓϩάϥϚʔਓੜॳΊͯͷΞϊςʔγϣϯ@Controllerʣ
͜Ε͔ΒGraphQLΛ ৮Δਓͷ͓ʹཱ͍ͯͰ͢
͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ
Appendix
ObjectMapperΛઃఆ͢Δ ͖᪴ɿ˒ˑˑˑˑ
GraphQL Spring Boot StarterσϑΥϧτͰ Date and Time APIΛϚοϐϯάग़དྷͳ͍ Spring Boot
2.0ʹରԠࡁ jackson-datatype-jsr310ೖ͍ͬͯΔͣ... URL: https://github.com/graphql-java/graphql-spring-boot/pull/63/commits/7eed5c83312c87b1c3fa73416399408570ac079c
ObjectMapperConfigurer͕͋ͬͨ @Component public class GraphQLObjectMapperConfig implements ObjectMapperConfigurer { @Override public
void configure(final ObjectMapper mapper) { mapper.registerModule(new JavaTimeModule()); } } URL: https://github.com/graphql-java/graphql-java-servlet/issues/42