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
250
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
Intent as Code
shoppingjaws
6
1k
手動確認はもう限界 〜XCUITestでCustom URL Schemeの遷移を起動種別ごとに自動テストする〜 / Testing Custom URL Schemes with XCUITest
otouto
0
310
AI が書く Go コードの品質を劇的に向上させる Linter: “declscope”
mpyw
0
370
Heart of Swift Concurrency
koher
0
910
App Intentsのビルドプロセスを支える技術
kntkymt
0
400
モジュールの視点からSwiftを読み解く #iosdc
s_shimotori
0
180
Webの地図
yosuke_furukawa
PRO
6
4.6k
Omarchy Tokyo やると聞いて UMPC 買ってセットアップしてきた
mtsmfm
0
150
UPDATE をやめる — EF Core でマスタをバージョン管理する
panda728
PRO
0
210
Security issues being discussed on Web Platforms
petamoriken
0
980
The Rails Doctrine Decade
koic
2
140
JPUG勉強会 OSSデータベースの内部構造を理解しよう(第2回)
oga5
0
260
Featured
See All Featured
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
3
3.8k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
1
610
Producing Creativity
orderedlist
PRO
348
41k
Optimizing for Happiness
mojombo
378
71k
From π to Pie charts
rasagy
1
370
Making the Leap to Tech Lead
cromwellryan
135
10k
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
460
The Language of Interfaces
destraynor
162
27k
Leo the Paperboy
mayatellez
10
2.3k
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.2k
Designing Powerful Visuals for Engaging Learning
tmiket
1
570
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
360
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