Lock in $30 Savings on PRO—Offer Ends Soon! ⏳
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
210
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
堅牢なフロントエンドテスト基盤を構築するために行った取り組み
shogo4131
8
2.3k
FluorTracer / RayTracingCamp11
kugimasa
0
230
著者と進める!『AIと個人開発したくなったらまずCursorで要件定義だ!』
yasunacoffee
0
130
【Streamlit x Snowflake】データ基盤からアプリ開発・AI活用まで、すべてをSnowflake内で実現
ayumu_yamaguchi
1
120
dotfiles 式年遷宮 令和最新版
masawada
1
760
Context is King? 〜Verifiability時代とコンテキスト設計 / Beyond "Context is King"
rkaga
9
1.1k
Giselleで作るAI QAアシスタント 〜 Pull Requestレビューに継続的QAを
codenote
0
170
実はマルチモーダルだった。ブラウザの組み込みAI🧠でWebの未来を感じてみよう #jsfes #gemini
n0bisuke2
2
860
チームをチームにするEM
hitode909
0
320
AtCoder Conference 2025「LLM時代のAHC」
imjk
2
460
20251127_ぼっちのための懇親会対策会議
kokamoto01_metaps
2
430
WebRTC と Rust と8K 60fps
tnoho
2
2k
Featured
See All Featured
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
1.8k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.3k
The Illustrated Children's Guide to Kubernetes
chrisshort
51
51k
What's in a price? How to price your products and services
michaelherold
246
13k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
5.8k
The Power of CSS Pseudo Elements
geoffreycrofte
80
6.1k
Building Applications with DynamoDB
mza
96
6.8k
Leading Effective Engineering Teams in the AI Era
addyosmani
8
1.3k
Build The Right Thing And Hit Your Dates
maggiecrowley
38
3k
Building a Scalable Design System with Sketch
lauravandoore
463
34k
Designing for Performance
lara
610
69k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
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