Slide 1

Slide 1 text

GraphQL͕ ֨޷ྑͦ͞͏ͩͬͨͷͰɺ SpringͰೖ໳ͨ͠Β͍᪴ͨ࿩

Slide 2

Slide 2 text

About me • ∁໺ ͸͍Ͷ • SoftBank Payment Service Corp. • ϓϩάϥϚʔ3೥ੜ
 ։ൃϓϩδΣΫτࢧԉ • I — Spring Framework! hainet hainet_g

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

“(GraphQL is) A query language for your API” ཉ͍͠΋ͷ͚ͩऔಘ͢Δ WebAPiͷ࢓૊Έ

Slide 5

Slide 5 text

ྫɿΫϨδοτΧʔυ৘ใΛऔಘ͢Δ

Slide 6

Slide 6 text

Χʔυ൪߸(number)Λऔಘ͢Δ

Slide 7

Slide 7 text

༗ޮظݶ(goodThru)΋औಘ͢Δ

Slide 8

Slide 8 text

ϒϥϯυ(brand)΋औಘ͢Δ ཁٻ͞ΕΔ·ͰόοΫΤϯυͰ ϒϥϯυΛऔಘ͢ΔΫΤϦ͸ಈ͔ͳ͍

Slide 9

Slide 9 text

3 Slides Cooking! with GraphQL Spring Boot Starter

Slide 10

Slide 10 text

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') }

Slide 11

Slide 11 text

2. Describe Schemas type CreditCard { id: ID! number: String! goodThru: String! securityCode: String! } type Query { findCreditCards: [CreditCard]! }

Slide 12

Slide 12 text

3. Implement Queries @Component public class CreditCardQuery implements GraphQLQueryResolver { public List findCreditCards() { // Find credit cards! return creditCards; } }

Slide 13

Slide 13 text

Run! https://github.com/hainet/ graphql-spring-boot-sample

Slide 14

Slide 14 text

খ͞ͳϓϩδΣΫτʹಋೖͨ͠ͷͰɺ GraphQLͷ֨޷ྑ͞Λڞ༗͍ͨ͠ʂ ʢͱࢥͬͯLTͷ४උΛ࢝Ί·ͨ͠ɻʣ

Slide 15

Slide 15 text

͕ɺࠓ೔͢Δ࿩͸ GraphQLͷ֨޷ྑ͍࿩ …Ͱ͸ͳ͍ɻ ྑ͍ॴ΋୔ࢁ͋ͬͯ঺հ͍ͨ͠… @ComponentΛ͚ͭΔ͚ͩͰେମԿͰ΋ग़དྷΔͱ͔ɺ Schemaͱ࣮૷͕Ұக͍ͯ͠ͳ͍ͱڭ͑ͯ͘ΕΔͱ͔ɺ ෆཁͳResolver͸ಈ͔ͳ͍(ෆཁͳSQL͸ྲྀΕͳ͍)ͱ͔ɺ LTͳͷͰׂѪ (´ʀωʀʆ)

Slide 16

Slide 16 text

࢖ͬͯΈͯ෼͔ͬͨ SpringϢʔβʔ͔ͩΒͦ᪴͘͜ ͱͯ΋஍ຯͳ࣮૷ͷ࿩Λ͠·͢ɻ

Slide 17

Slide 17 text

࣮૷Ͱ͍᪴ͨ͜ͱ 1. PayloadΛόϦσʔγϣϯ͢Δ
 ͖᪴౓ɿ˒˒˒ˑˑ 2. ΤϥʔϋϯυϦϯά͢Δ
 ͖᪴౓ɿ˒˒˒˒˒˒˒˒˒˒ 3. ObjectMapperΛઃఆ͢Δ(Appendix)
 ͖᪴౓ɿ˒ˑˑˑˑ

Slide 18

Slide 18 text

PayloadΛόϦσʔγϣϯ͢Δ
 ͖᪴౓ɿ˒˒˒ˑˑ

Slide 19

Slide 19 text

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; } }

Slide 20

Slide 20 text

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; } }

Slide 21

Slide 21 text

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; } }

Slide 22

Slide 22 text

ͷւ͸ɹɹ༏੎ ΫϥΠΞϯτ αΠυͰʂ UI/UX! ଈ࣌ Ϩεϙϯεʂ ͦ ͏ ͡ Ό Ͷ ͐ ʂ ͷຽ͕જΔʹ͸աࠅͳ؀ڥ

Slide 23

Slide 23 text

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ͱ͸ ͜͜Ͱ όϦσʔγϣϯ͢Δ

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

input CreditCardPayload { number: Number! goodThru: GoodThru! brand: Brand! securityCode: SecurityCode! } Number.java CreditCard.graphqlsఆٛมߋ GoodThru.java SecurityCode.java

Slide 26

Slide 26 text

όϦσʔγϣϯͰ͖ͨʂ DDDతͳఆٛ΋៉ྷʂ ͚ͩΕͲ...

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

ཉ͔ͬͨ͠ͷ͸ ͜Ε͡Όͳ͍

Slide 29

Slide 29 text

ΤϥʔϋϯυϦϯά͢Δ
 ͖᪴౓ɿ˒˒˒˒˒˒˒˒˒˒

Slide 30

Slide 30 text

Τϥʔ࣌ɺGraphQL Spring Boot Starter͸ ͱΓ͋͑ͣInternal Server ErrorΛฦ͢ HandlerExceptionResolver΋͢Γൈ͚Δ...

Slide 31

Slide 31 text

Τϥʔϋϯυϥͷ࣮૷Λ௥ͬͯɺ ॲཧΛࠩ͠ସ͑ͯΈͨ @Override public List processErrors( List errors) { errors.add( new GenericGraphQLError( "Internal Server Error(s) while executing query" ) ); // Omitted return errors; } DefaultGraphQLErrorHandler.java

Slide 32

Slide 32 text

Τϥʔϋϯυϥͷ࣮૷Λ௥ͬͯɺ ॲཧΛࠩ͠ସ͑ͯΈͨ @Override public List processErrors( List errors) { errors.add( new GenericGraphQLError( "Internal Server Error(s) while executing query" ) ); // Omitted return errors; } MyGraphQLErrorHandler.java

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

ʊਓਓਓਓਓਓਓਓਓʊ ʼɹ Stack Trace ɹʻ ʉY^Y^Y^Y^Y^Y^Yʉ

Slide 35

Slide 35 text

Internal Server Error Ͳ͜Ζͷ૽͗Ͱ͸ͳ͍

Slide 36

Slide 36 text

GraphQLErrorΛ෼ྨͯ͠… GraphQLError͸େ͖͘3छྨ

Slide 37

Slide 37 text

GraphQLErrorΛద੾ʹϋϯυϦϯά͢Δ GraphQLErrorHandlerΛ࣮૷ @Component @Slf4j public class GraphQLErrorHandlerConfig implements GraphQLErrorHandler { @Override public List processErrors(final List 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; } }

Slide 38

Slide 38 text

@Component @Slf4j public class GraphQLErrorHandlerConfig implements GraphQLErrorHandler { @Override public List processErrors(final List 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Λ࣮૷

Slide 39

Slide 39 text

GraphQLErrorHolderΛ࡞੒ public class GraphQLErrorHolder implements GraphQLError { private GraphQLError error; public GraphQLErrorHolder( final GraphQLError error) { this.error = error; } // Override getters } ThrowableΛӅṭ

Slide 40

Slide 40 text

׬੒ʂ ೚ҙͷϝοηʔδ ೚ҙͷΤϥʔλΠϓ ೚ҙͷkey/value

Slide 41

Slide 41 text

ここまで躓いて 私は考えた

Slide 42

Slide 42 text

何か⼤きな勘違いを しているのではないか... Hibernate Validatorは利かない, HandlerExceptionResolverはすり抜ける, ObjectMapperは独⾃のインターフェースを通して登録する. 共通点は? 情報が少なすぎて調査が難航。 上司からもJavaでGraphQL誰もやっていないとツッコミを受ける。

Slide 43

Slide 43 text

/graphqlΤϯυϙΠϯτ͸ Spring MVCͷ֎ͷੈքͷॅਓͩͬͨ ʲग़དྷͳ͍͜ͱʳ ɾHandlerInterceptor ɾetc… GraphQLWebAutoConfiguration.java ʲग़དྷΔ͜ͱʳ ɾSpring AOP ɾFilter ɹˠ Spring Security͸OK!

Slide 44

Slide 44 text

Springͷੈք͔͠஌Βͳ͔ͬͨͷͰ େ͖͘ζοίέ·ͨ͠... ɹɹɹɹɹɹɹ㱯ɹ㱯 ɹɹɹɹɹʢŋТŋccɹɹζίʔ ɹɹɹɹɹɹɹcɹɹc ɹɹɹɹɹ㱬㱬@@@@ϊɹʹj ʢϓϩάϥϚʔਓੜॳΊͯͷΞϊςʔγϣϯ͸@Controllerʣ

Slide 45

Slide 45 text

͜Ε͔ΒGraphQLΛ ৮Δਓͷ͓໾ʹཱͯ͹޾͍Ͱ͢

Slide 46

Slide 46 text

͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ

Slide 47

Slide 47 text

Appendix

Slide 48

Slide 48 text

ObjectMapperΛઃఆ͢Δ
 ͖᪴౓ɿ˒ˑˑˑˑ

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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