Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
2018.4.18 - GraphQLが格好良さそうだったので、Springで入門したら躓いた話
Search
hainet50b
April 18, 2018
Programming
2
1.2k
2018.4.18 - GraphQLが格好良さそうだったので、Springで入門したら躓いた話
▼開催日▼
2018年4月18日
▼イベント▼
JSUG勉強会 2018年 その3
https://jsug.doorkeeper.jp/events/73144
hainet50b
April 18, 2018
Tweet
Share
More Decks by hainet50b
See All by hainet50b
2023.6.23 - AWS Dev Day 2023 Tokyo Spring Boot 3.0 クラウドネイティブ関連機能 & JSUG(日本Springユーザ会)活動紹介
hainet50b
0
220
2023.2.17 - Spring Boot 3.0 オブザーバビリティツアーガイド
hainet50b
3
1.7k
2021.12.3 - 決済システムで学ぶレジリエントなサービスのいろは
hainet50b
13
4.8k
2019.10.4 - 決済トランザクションの監視におけるElastic Stackの活用 ~アプリケーションロギング/トレーシングの可視化~
hainet50b
5
2.6k
Other Decks in Programming
See All in Programming
OCaml 5でモダンな並列プログラミングを Enjoyしよう!
haochenx
0
140
今こそ知るべき耐量子計算機暗号(PQC)入門 / PQC: What You Need to Know Now
mackey0225
3
370
AIエージェント、”どう作るか”で差は出るか? / AI Agents: Does the "How" Make a Difference?
rkaga
4
2k
責任感のあるCloudWatchアラームを設計しよう
akihisaikeda
3
170
フロントエンド開発の勘所 -複数事業を経験して見えた判断軸の違い-
heimusu
7
2.8k
360° Signals in Angular: Signal Forms with SignalStore & Resources @ngLondon 01/2026
manfredsteyer
PRO
0
120
Architectural Extensions
denyspoltorak
0
280
高速開発のためのコード整理術
sutetotanuki
1
400
Implementation Patterns
denyspoltorak
0
290
Vibe Coding - AI 驅動的軟體開發
mickyp100
0
170
カスタマーサクセス業務を変革したヘルススコアの実現と学び
_hummer0724
0
700
組織で育むオブザーバビリティ
ryota_hnk
0
170
Featured
See All Featured
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
170
Design in an AI World
tapps
0
140
Everyday Curiosity
cassininazir
0
130
AI Search: Implications for SEO and How to Move Forward - #ShenzhenSEOConference
aleyda
1
1.1k
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
130
Ruling the World: When Life Gets Gamed
codingconduct
0
140
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
340
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.4k
Rails Girls Zürich Keynote
gr2m
96
14k
The Curious Case for Waylosing
cassininazir
0
230
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
200
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