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
190
2023.2.17 - Spring Boot 3.0 オブザーバビリティツアーガイド
hainet50b
3
1.6k
2021.12.3 - 決済システムで学ぶレジリエントなサービスのいろは
hainet50b
13
4.7k
2019.10.4 - 決済トランザクションの監視におけるElastic Stackの活用 ~アプリケーションロギング/トレーシングの可視化~
hainet50b
5
2.6k
Other Decks in Programming
See All in Programming
TDD 実践ミニトーク
contour_gara
0
150
RDoc meets YARD
okuramasafumi
1
120
学習を成果に繋げるための個人開発の考え方 〜 「学習のための個人開発」のすすめ / personal project for leaning
panda_program
1
110
WebAssemblyインタプリタを書く ~Component Modelを添えて~
ruccho
1
910
兎に角、コードレビュー
mitohato14
0
150
「リーダーは意思決定する人」って本当?~ 学びを現場で活かす、リーダー4ヶ月目の試行錯誤 ~
marina1017
0
240
Oracle Database Technology Night 92 Database Connection control FAN-AC
oracle4engineer
PRO
1
210
AIレビュアーをスケールさせるには / Scaling AI Reviewers
technuma
2
230
Jakarta EE Core Profile and Helidon - Speed, Simplicity, and AI Integration
ivargrimstad
0
200
フロントエンドのmonorepo化と責務分離のリアーキテクト
kajitack
2
140
The State of Fluid (2025)
s2b
0
200
Rancher と Terraform
fufuhu
0
110
Featured
See All Featured
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.8k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
358
30k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
18
1.1k
Unsuck your backbone
ammeep
671
58k
Rebuilding a faster, lazier Slack
samanthasiow
83
9.1k
Fantastic passwords and where to find them - at NoRuKo
philnash
51
3.4k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
48
9.6k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
26k
Building a Scalable Design System with Sketch
lauravandoore
462
33k
Navigating Team Friction
lara
189
15k
BBQ
matthewcrist
89
9.8k
Building Flexible Design Systems
yeseniaperezcruz
328
39k
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