ããã«ã¡ã¯ãã¨ã ã¹ãªã¼ ã¨ã³ã¸ãã¢ãªã³ã°ã°ã«ã¼ã ãã«ãããã¤ã¹ãã¼ã ã®è¤åã§ãã
æ¨å¹´æ«ã«å»å¸«åãã®ã¹ããã¢ããªãæ°ãã«ãªãªã¼ã¹ãã¾ããã ã¹ããã¢ããªåãã® BFF(Backends For Frontends) ãæ°è¦ã«éçºããã®ã§ãããããã«ã¯ SpringBoot + Kotlin + GraphQL ãªã¢ããªã±ã¼ã·ã§ã³ãæ¡ç¨ãã¦ãã¾ãã
GraphQL ã¯ãã¼ã ã§ã®æ¡ç¨ã¯åãã¦ã§ãç§ããã®ããã¸ã§ã¯ãã§åãã¦è§¦ãã¾ããã
ãã®ãããªç¶æ³ã ã£ãã®ã§ GraphQL å¨ãã«ã¤ãã¦ã¯è©¦è¡é¯èª¤ãéãããã¨ã¨ãªã£ãã®ã§ãããä»åã¯ãã®éçºã®ä¸ã§è¦ãã¦ãã ãã©ã¯ãã£ã¹ ãããã¤ãç´¹ä»ãããã¨æãã¾ãã
ãããã SpringBoot + Kotlin + GraphQL ãªéçºããããæ¹ã®åèã«ãªãã°å¹¸ãã§ãã
ï¼Godbolemandar [CC BY-SA 4.0], ã¦ã£ãã¡ãã£ã¢ã»ã³ã¢ã³ãºããï¼
- ã¹ã«ã©ã¼åã®å ¥åãã§ãã¯ã¯ Custom Scalar ã使ãã
- èªè¨¼çµæ㯠GraphQLContext ã«ä¿åããã
- ã¦ã¼ã¶æ å ±ã®ãã£ã¼ã«ãã«ããã¹ããã®ã¨ãããããªããã®
- èªè¨¼ãå¿ è¦ã§ãããã¨ã¯ Directive ã§è¡¨ç¾ããã
- ã¨ã©ã¼ãã³ããªã³ã°
- ãã£ã¼ã«ã㧠Nullable or Non-null ãè¿·ããããªã Nullable
- ãã£ã¼ã«ãã®å¼æ°ã§ Nullable or Non-null ãè¿·ããããªã Non-null
- è¦ç´ 追å ã®å¯è½æ§ããã Enum ã使ãã¨ãã¯ç´°å¿ã®æ³¨æã
- å 人ã®ç¥æµãåãã
- We are hiring
â» ãã®è¨äºã«ç»å ´ãã graphql-java ä¾åã®ã¯ã©ã¹çã¯ä»¥ä¸ã®ã©ã¤ãã©ãªã¨ãã¼ã¸ã§ã³ãå ã«ãã¦ãã¾ãã
- graphql-java 13.0 *2
- graphql-java-servlet 8.0.0 *3
- graphql-java-tools 5.6.1 *4
- graphql-spring-boot-starter 5.10.0 *5
ã¹ã«ã©ã¼åã®å ¥åãã§ãã¯ã¯ Custom Scalar ã使ãã
GraphQL ã¹ãã¼ãã®ãã£ã¼ã«ãã«ã¯å¼æ°ã渡ããã¨ãã§ãã¾ãããå®è¡æã«å ¥åãã§ãã¯ããããå ´åãããã¾ãã GraphQL ã¯ã¨ãªãçºè¡ããã¨å¯¾å¿ãããªã¾ã«ãã¼ãå®è¡ãããã®ã§ãæç´ã«ããã¨ãªã¾ã«ãã¼ã§å ¥åãã§ãã¯ã®ãã¸ãã¯ãå®è£ ãããã¨ã«ãªãã¾ãã
以ä¸ã¯ã¡ãã»ã¼ã¸ä¸è¦§ããã¼ã¸ãã¼ã·ã§ã³ã§åå¾ããã¹ãã¼ãã¨ãªã¾ã«ãã¼ã®ä¾ã§ãã
type Query { # ã¡ãã»ã¼ã¸ä¸è¦§ãåå¾ãã messages( # åå¾ä»¶æ° first: Int! # æå®ããæååã表ãã¡ãã»ã¼ã¸ä»¥éãåå¾ãã after: String ): [Message!] }
class QueryResolver : GraphQLQueryResolver { fun messages(first: Int, after: String?): List<Message> { // first ã«100以ä¸ã®æ°å¤ãæå®ããããã¨ã©ã¼ã«ããã if (first > 100) throw IllegalArgumentException() ... } }
ãªã¾ã«ãã¼ã§åå¾ä»¶æ°ã®ä¸éãã§ãã¯ãè¡ãªã£ã¦ãã¾ãããä»ã®ãªã¾ã«ãã¼ã§ãåããããªå ¥åãã§ãã¯ãä½åãå®è£ ããªãã¨ãããªããªããããããããã¾ããããªãåãããã¾ãã
GraphQL ã§ã¯ç¬èªã«ä»»æã®ã¹ã«ã©ã¼åãå®ç¾©ãããã¨ãã§ããã¹ã«ã©ã¼åã«å
¥åãã§ãã¯ãå®è£
ãããã¨ã§ãªã¾ã«ãã¼ã§å
¥åãã§ãã¯ãããå¿
è¦ããªããªãã¾ãã
ã¹ã«ã©ã¼åã§å
¥åãã§ãã¯ããããã«ããå ´åãã¹ãã¼ãã¨ãªã¾ã«ãã¼ã®å®è£
ã¯ä»¥ä¸ã®ããã«ãªãã¾ãã
# ãã¼ã¸ãã¼ã·ã§ã³ç¨ã®éæå®å(1ãã99ã®æ°å¤) scalar PaginationAmount type Query { # ã¡ãã»ã¼ã¸ä¸è¦§ãåå¾ãã messages( # åå¾ä»¶æ° first: PaginationAmount! # æå®ããæååã表ãã¡ãã»ã¼ã¸ä»¥éãåå¾ãã after: String ): [Message!] }
typealias PaginationAmount = Int // ã¨ã¤ãªã¢ã¹ã§ã¹ãã¼ãä¸ã¨ååãåãããã¨å³ããã class QueryResolver : GraphQLQueryResolver { fun messages(first: PaginationAmount, after: String?): List<Message> { // ãã®å¦çãå®è¡ãããæç¹ã§ first ã 100æªæºã§ãããã¨ã¯ä¿è¨¼ããã ... } }
ç¬èªã®ã¹ã«ã©ã¼åãä½æããæ¹æ³ã¯ä»¥ä¸ã®2ã¹ãããã§ãã
Coercing
ã¤ã³ã¿ãã§ã¼ã¹ãå®è£ ããã¯ã©ã¹ãä½æãã- ä½æãã
Coercing
ãå ã«GraphQLScalarType
ãä½æããBean ã«ç»é²ãã
class PaginationAmountCoercing : Coercing<Int, Int> { override fun parseLiteral(input: Any): Int? { // å ¥åãã§ãã¯ã失æãããå ´å㯠CoercingParseLiteralException ã throw ãã ... } ... } @Bean val PaginationAmount = GraphQLScalarType.newScalar() .name("PaginationAmount") .description("A positive integer with an upper bound") .coercing(PaginationAmountCoercing()) .build()!!
èªè¨¼çµæ㯠GraphQLContext ã«ä¿åããã
SpringBoot ããã¼ã¹ã«ã¢ããªã±ã¼ã·ã§ã³ãä½ã£ã¦ããã¨èªè¨¼çµæã®ã¦ã¼ã¶æ å ±ã¯ãªã¯ã¨ã¹ãã¹ã³ã¼ãã® Bean ã«ä¿åãããããªå½¢ã«ãªãããã§ããã GraphQL ã«ã¯åä¸ãªã¯ã¨ã¹ãã§ä½¿ãã¾ãããã¨ãã§ããã³ã³ããã¹ãã®ä»çµã¿ãç¨æããã¦ãã¾ãã
AuthContext
ã¨ããç¬èªã®ã¯ã©ã¹ã« User ãæãããä¾ã¯ä»¥ä¸ã®ããã«ãªãã¾ãã
GraphQLContext
ã¤ã³ã¿ãã§ã¼ã¹ãå®è£ ããAuthContext
ã¯ã©ã¹ãä½æbuild
ã¡ã½ããã§AuthContext
ã®ã¤ã³ã¹ã¿ã³ã¹ãè¿ããããªGraphQLContextBuilder
ãä½æã Bean ã«ç»é²
class AuthContext(val user: User?) : GraphQLContext { ... } @Bean fun authContextBuilder(): GraphQLContextBuilder = object : DefaultGraphQLContextBuilder() { override fun build( request: HttpServletRequest, response: HttpServletResponse ): GraphQLContext { // ã¦ã¼ã¶ã®æ å ±ãåå¾ val user = ... return AuthContext(user) } }
AuthContext
ã®çæã¯ãªã¯ã¨ã¹ããã¨ã«1åå®è¡ããã¦ãçµæã¯ãªã¾ã«ãã¼ã§åå¾ãããã¨ãã§ãã¾ãã
class QueryResolver : GraphQLQueryResolver { fun messages( first: PaginationAmount, after: String?, environment: DataFetchingEnvironment // å¼æ°ã«æå®ãããã¨ã§ãããããã³ã³ããã¹ãã®åå¾ãã§ãã ): List<Message> { val authContext = environment.getContext<AuthContext>() val user = authContext.user ... } }
ã¦ã¼ã¶æ å ±ã®ãã£ã¼ã«ãã«ããã¹ããã®ã¨ãããããªããã®
èªè¨¼æ¸ã¿ã¦ã¼ã¶å°ç¨ã®ãããããã³ã³ãã³ãããã¹ãã¼ãã§è¡¨ç¾ãããã¨ããã¨ãã¦ã¼ã¶æ å ±ã®åã«ããããã³ã³ãã³ãã®ãã£ã¼ã«ãã追å ããå½¢ãèãããã¾ãã
type Query { # ç¾å¨ã®ã¦ã¼ã¶ï¼æªèªè¨¼ã®å ´å㯠nullï¼ currentUser: User } # ã¦ã¼ã¶æ å ± type User { # ä¼å¡ã®åå name: String! ... # ããããã³ã³ãã³ãä¸è¦§ recommendedContents: [RecommendedContent] }
ã¦ã¼ã¶æ å ±ã¨ããããã³ã³ãã³ããåå¾ããã¯ã¨ãªã¯ä»¥ä¸ã®ããã«ãªãã¾ãã
query { currentUser { name recommendedContents { ... } } }
facebook ãæä¾ãã¦ãã GraphQL ã®ãµã³ãã«*6ã¨åãè¨è¨æ¹éã¨ãªã£ã¦ãã¦ä¸è¦èªç¶ãªå¯¾å¿ã«è¦ãã¾ããã ä»å¾ã®æ©è½è¿½å ã®æ¹åã«ãã£ã¦ã¯åé¡ã«ãªã£ã¦ããå¯è½æ§ãããã¾ãã ä¾ãã°ãæªèªè¨¼ã®ã¦ã¼ã¶ã«å¯¾ãã¦ãããããã³ã³ãã³ããè¿ããªããã°ãªããªããªãããããã¾ããã ããããã³ã³ãã³ãä¸è¦§ãä¼å¡æ å ±ã®ãã£ã¼ã«ãã¨ãã¦ããã¨ãä¼å¡ã§ã¯ãªãã¦ã¼ã¶ã«ã¤ãã¦ã¯ãã®ãã£ã¼ã«ãã«ã¢ã¯ã»ã¹ãããã¨ãã§ããªããªã£ã¦ãã¾ãã¾ãã
ãã®ãããªå ´åã¯ãä¼å¡æ å ±ã¨ããããã³ã³ãã³ãã®ãªã¬ã¼ã·ã§ã³ãæã¤ã®ãã·ã³ãã«ã§ãã ã¹ãã¼ãå®ç¾©ã®ä¸é¨ãä¿®æ£ãããã®ã¯ä»¥ä¸ã®ããã«ãªãã¾ãã
type Query { # ç¾å¨ã®ã¦ã¼ã¶ currentUser: User # ããããã³ã³ãã³ãä¸è¦§ recommendedContents: [RecommendedContent] }
ã¦ã¼ã¶æ å ±ã®ãã£ã¼ã«ãã«ãããã®ã¯
- ã¦ã¼ã¶ãæ§æãã¦ããè¦ç´ ã§ããï¼e.g. ã¡ã¼ã«ã¢ãã¬ã¹ãªã©ã®ç»é²æ å ±ï¼
- ã¦ã¼ã¶ã®ç¶æ ã表ãè¦ç´ ã§ããï¼e.g. ä¿æãã¤ã³ãæ°ï¼
- ä»ã®ã¦ã¼ã¶ãããé¢é£ãè¦ããå¿ è¦ãããï¼e.g. SNSã®åéä¸è¦§ï¼
ã®ããããã®æ¡ä»¶ãæºãããã®ã«ããã®ãè¯ãã§ãããã
èªè¨¼ãå¿ è¦ã§ãããã¨ã¯ Directive ã§è¡¨ç¾ããã
æªèªè¨¼ã®å ´åã«çµæãè¿ããªããã£ã¼ã«ãããããã¯åã§ãããã¨ã示ããããã¨ãããã¾ãã ãã®ãããªæ㯠Directive ã¨ããæ©è½ã使ãã°å®£è¨çã«æ å ±ãä»å ãããã¨ãã§ãã¾ãã
# èªè¨¼æ¸ã¿ã®å ´åã®ã¿ã¢ã¯ã»ã¹å¯è½ directive @auth on OBJECT | FIELD_DEFINITION type User @auth { name: String! }
å®è¡æã®æ¯ãèããå¤ãããã㪠Directive ã®å®è£ æ¹æ³ã¯ä»¥ä¸ã®2ã¹ãããã§ãã
SchemaDirectiveWiring
ã¤ã³ã¿ãã§ã¼ã¹ãå®è£ ããã¯ã©ã¹ãä½æããSchemaDirective
ã¨ã㦠Bean ã«ç»é²ãã
èªè¨¼ãå¿ è¦ãªãã¨ã示ã Directive ã®å®è£ ã¯ä»¥ä¸ã®ããã«ãªãã¾ãã
class AuthDirective : SchemaDirectiveWiring { override fun onField(environment: SchemaDirectiveWiringEnvironment<GraphQLFieldDefinition>): GraphQLFieldDefinition { val originalDataFetcher = environment.fieldDataFetcher val authDataFetcher = DataFetcher<Any> { dataFetchingEnvironment -> val authContext = dataFetchingEnvironment.getContext<AuthContext>() if (authContext.user != null) { // èªè¨¼æ¸ã¿ã®å ´åãå ã®ãªã¾ã«ãã¼ãå¼ã³åºã originalDataFetcher.get(dataFetchingEnvironment) } else { // æªèªè¨¼ã®å ´åã ... } } return environment.setFieldDataFetcher(authDataFetcher) } } @Bean fun directives(): List<SchemaDirective> = listOf( SchemaDirective("auth", AuthDirective()) )
ã¨ã©ã¼ãã³ããªã³ã°
graphql-java ã§ã®ã¨ã©ã¼ãã³ããªã³ã°ã®æ¹æ³ã¯ããã¤ãããã¾ããã GraphQLErrorHandler
ãã«ã¹ã¿ãã¤ãºããæ¹æ³ãç´¹ä»ãã¾ãã
ããã©ã«ãã§ã¯ DefaultGraphQLErrorHandler
ã使ãããããã«ãªã£ã¦ãã¦ããªã¾ã«ãã¼ããã¹ãã¼ãããä¾å¤ã¯ "Internal Server Error(s) while executing query" ã¨ããã¡ãã»ã¼ã¸ã®1ã¤ã®ã¨ã©ã¼ã«éç´ããã¦ãã¾ã詳細ä¸æã¨ãªã£ã¦ãã¾ãã¾ãããèªç±ã«ã«ã¹ã¿ãã¤ãºãããã¨ãå¯è½ã§ãã
å®è£
æ¹æ³ã¯ GraphQLErrorHandler
ã¤ã³ã¿ãã§ã¼ã¹ãå®è£
ããã¯ã©ã¹ãä½æããBean ã«ç»é²ããã®ã¿ã§ãã
@Component class CustomGraphQLErrorHandler : GraphQLErrorHandler { override fun processErrors(errors: List<GraphQLError>): List<GraphQLError> { // ã¨ã©ã¼ãã³ããªã³ã°ãå®è£ ãã ... } }
GraphQLErrorHandler
ãã«ã¹ã¿ãã¤ãºãã以å¤ã®æ¹æ³ã§ã¯ã
- SpringBoot ã®
@ExceptionHandler
ã¢ããã¼ã·ã§ã³ã使ãæ¹æ³*7 GraphQLError
ã¤ã³ã¿ãã§ã¼ã¹ãå®è£ ããä¾å¤ãã¹ãã¼ããæ¹æ³
ãªã©ãããã¾ããããããã¨æ¯ã¹ãã¨
- GraphQL ã®æ¨æºçãªã¨ã©ã¼è¡¨ç¾ã§ãã
path
ãlocations
ã®æ å ±ãããã©ã«ãã§è¨å®ããã¦ããï¼ä»ã®æ¹æ³ã§ã¯ç¬èªå®è£ ãå¿ è¦ï¼ - ããªãã¼ã·ã§ã³ã¨ã©ã¼ã«ã¤ãã¦ãã«ã¹ã¿ãã¤ãºå¯è½
- ã¨ã©ã¼ã®æ°ãã«ã¹ã¿ãã¤ãºå¯è½ï¼ã¬ã¹ãã³ã¹JSONã®
errors
ãã£ã¼ã«ãã«ä»»æã®è¦ç´ æ°ã§æ ¼ç´ã§ããï¼
ãªã©ã®ã¡ãªãããããã¾ããã©ãã¾ã§ã«ã¹ã¿ãã¤ãºãããã次第ãªã¨ãããããã¾ãããããããä¸çªèªç±åº¦ãé«ãã«ã¹ã¿ãã¤ãºæ¹æ³ã§ãã
ãã£ã¼ã«ã㧠Nullable or Non-null ãè¿·ããããªã Nullable
Non-null ã§ããå¿ è¦ããªããã£ã¼ã«ãã Non-null ã§å®ç¾©ãã¦ãã¾ãã¨ãåå¾ã§ããã¯ãã®ãã¼ã¿ãè¿ããªããªãå¯è½æ§ãããã¾ãã
å ã»ã©ãä¾ã«åºããã¹ãã¼ããä¾ã«ãã¦èª¬æãã¾ãã
# Schema type Query { # ç¾å¨ã®ã¦ã¼ã¶ currentUser: User # ããããã³ã³ãã³ãä¸è¦§ recommendedContents: [RecommendedContent] }
# Query query { currentUser { name } recommendedContents { title } }
ä¸è¨ã®ãããªã¯ã¨ãªãçºè¡ããéã«ä½ãããã¨ã©ã¼ãçºçããããããã³ã³ãã³ãã®æ å ±ãåå¾ã§ãã以ä¸ã®ãã㪠JSON ãåå¾ã§ããã¨ãã¾ãã
{ "data": { "currentUser" : { "name": "ã¨ã ã¹ãªã¼ 太é" }, "recommendedContents": null }, "errors": [ { "message": "failed to get recommended contents title", "path": ["recommendedContents"] } ] }
ãã®æããã recommendedContents
ã®åã [RecommendedContent]!
ã®ããã« Non-null ã ã£ãå ´åãnull
ã«ãªãã¯ãã ã£ããã£ã¼ã«ãã®è¦ªã®ãªãã¸ã§ã¯ãã null
ã«ãªãã¾ããã¤ã¾ããã®å ´å㯠data
ã null
ã«ãªããåå¾ã§ãã¦ããã¯ãã® currentUser
ã®ãã¼ã¿ãããã¯ã©ã¤ã¢ã³ãã«è¿ããªããªãã¾ãã
{ "data": null, "errors": [ { "message": "failed to get recommended contents title", "path": ["recommendedContents"] } ] }
ï¼data
ãã£ã¼ã«ãã¯æä¸ä½ã®ãã£ã¼ã«ã㧠Nullable ã§ããï¼
ä¸è¨ã®ãããªã±ã¼ã¹ãèããããããã Nullable ã Non-null ãè¿·ã£ãæ㯠Nullable ã¨ããã®ãè¯ãã¨æããã¾ãã
ã¾ããè¤æ°ã®ã¯ã¨ãªãåæã«åãåãããããæã®ãã¨ãèããã¨ãQuery
ããã³ Mutation
é
ä¸ã®ãã£ã¼ã«ã㯠Nullable ã«ãã¦ããã®ãç¡é£ãªã®ããããã¾ããã
Nullå¯å¦ã«ã¤ãã¦ã®èå¯ã¯ãã¡ãã®è¨äº*8ãã¨ã¦ãåèã«ãªãã¾ããã
ã¨ã©ã¼ã¨ãªã£ãå ´åãä¾ã«åºãã¦å°ã詳ããè¦ã¦ã¿ã¾ããããäºææ§ã®è¦³ç¹ã§ã Nullable ã®æ¹ãæã¾ããã§ãã
GraphQLãã¹ããã¢ããªã®APIã¨ãã¦åããã¤ã¤ã¹ãã¼ãå®ç¾©ãå¤æ´ãããã¨ãèãã¾ãããã®æããã§ã«ãªãªã¼ã¹ããã¦ãããã¼ã¸ã§ã³ã®ã¢ããªã®äºææ§ãä¿ã¡ã¤ã¤å¤æ´ããå¿
è¦ãåºã¦ãã¾ãã
Non-null ãã Nullable ã«å¤æ´ããå ´å
æ§ãã¼ã¸ã§ã³ã®ã¢ããªã§ã¯ Non-null ãªå¤ã®ã¿åãä»ãã
-> ãµã¼ãããnull
ãè¿ã£ã¦ããã¨ã¯ã©ãã·ã¥ããå¯è½æ§ãããã®ã§å±éºï¼Nullable ãã Non-null ã«å¤æ´ããå ´å
æ§ãã¼ã¸ã§ã³ã®ã¢ããªã§ã¯ Nullable ãªå¤ãåãä»ãã
-> ãµã¼ããnull
ãè¿ããªããªã£ã¦ãåé¡ãªãã®ã§å®å ¨
ãã£ã¼ã«ãã®å¼æ°ã§ Nullable or Non-null ãè¿·ããããªã Non-null
åã®ã»ã¯ã·ã§ã³ã§ã¯ãµã¼ãããã®ã¬ã¹ãã³ã¹ã«ã¤ãã¦ã¯è¿·ããããªã Nullable ã¨ãããã¨ãè¿°ã¹ã¾ãããã ã¯ã©ã¤ã¢ã³ãããéãããå¤ã«ã¤ãã¦ã¯éã®ãã¨ãè¨ãã¾ããã¤ã¾ããNullable or Non-null ãè¿·ããããªã Non-nullãã§ãã
äºææ§ã®è¦³ç¹ã§åã³æ´çããã¨ä»¥ä¸ã®ããã«ãªãããã§ãã
Non-null ãã Nullable ã«å¤æ´ããå ´å
æ§ãã¼ã¸ã§ã³ã®ã¢ããªãã Non-null ãªãã©ã¡ã¼ã¿ã®ã¿éããã
-> ãµã¼ããnull
ãåãåããããã«ãªã£ã¦ãåé¡ãªãã®ã§å®å ¨Nullable ãã Non-null ã«å¤æ´ããå ´å
æ§ãã¼ã¸ã§ã³ã®ã¢ããªãã Nullable ãªãã©ã¡ã¼ã¿ãéããã
-> ãµã¼ããnull
ãåãåããªããªãã¨ã¯ã¨ãªå®è¡ã§ããªãå¯è½æ§ãããã®ã§å±éºï¼
è¦ç´ 追å ã®å¯è½æ§ããã Enum ã使ãã¨ãã¯ç´°å¿ã®æ³¨æã
Enum ã®è¦ç´ 追å ã¯ãµã¼ãã¨ã¯ã©ã¤ã¢ã³ãã§éäºæã«ãªãå¯è½æ§ãããã¾ãã
ãµã¼ãå´ã¯æ°ãã Enum è¦ç´ ãå®è£
ãããã¼ã¸ã§ã³ããªãªã¼ã¹ããã°ããã§ãããã¯ã©ã¤ã¢ã³ãå´ï¼ã¹ããã¢ããªï¼ã¯ãæ°ãã Enum è¦ç´ ã解éã§ãããã¼ã¸ã§ã³ã®ã¢ããªããªãªã¼ã¹ããã¦ã¼ã¶ã®ç«¯æ«ã«æ°ãããã¼ã¸ã§ã³ã®ã¢ããªããã¦ã³ãã¼ããã¦ãããã¨ããã¾ã§ãå¿
è¦ã«ãªãã¾ãã
Enum ãã¯ã¨ãªã®çµæã¨ãã¦ä½¿ãå ´åãè¦ç´ 追å ãããåã®ã¹ãã¼ãå®ç¾©ãå
ã«ã³ã¼ããçæããæ§ãã¼ã¸ã§ã³ã®ã¢ããªã§ã¯ãµã¼ãããè¿ãããæ°ãã Enum è¦ç´ ã解éã§ããªãããã§ãã
ã¢ããªã®å¼·å¶ã¢ãããã¼ãæ©è½ã«ãã£ã¦è§£æ¶ããã¨ããæ段ãããã¾ãããã¨ã³ã¸ãã¢ã®é½åã§ãããããã®ã¯é¿ãããã§ãã
4ã¤ç®ã®è¦ç´ ã追å ããããã¨ãã¦ãã Enum ãä¾ã«ãã¦ãããã¤ã解決çãããã¾ãã
enum UserStatus { # å»å¸« DOCTOR # å»å¦ç MEDICAL_STUDENT # è¬å¤å¸« PHARMACIST # è¬å¦çï¼ãããã追å ããã. å¤ããã¼ã¸ã§ã³ã®ã¢ããªã¯ãã®å¤ãç¥ããªãï¼ # PHARMACY_STUDENT }
解決ç1. unknown ã ã£ãå ´åã«ã©ãããã決ãã
Appoloã¯ã©ã¤ã¢ã³ãã®å ´åãæªå®ç¾©ã® Enum ãè¿ã£ã¦ããã¨ãã«ã¯ã©ãã·ã¥ãããªãããã« unknown ã¨ããç¶æ ã«ã§ãã¾ãã unknown ãªã¨ãã«ã©ãããã決ãããã¨ãã§ããã°ç¹ã«åé¡ã«ã¯ãªããªãããããã¾ããã
解決ç2. ãã£ã¼ã«ããå¤ãã
è¦ç´ 追å ããããã¨ã«ãã£ã¼ã«ããå¤ããã¨ãã対çãåãã¾ãã
type User { # è¬å¦çãå«ã¾ãªãã¹ãã¼ã¿ã¹ status: UserStatus! # è¬å¦çãå«ãæ°ããã¹ãã¼ã¿ã¹ï¼ããããã£ã¼ã«ãåã®ãã¼ãã³ã°ã¯ã¨ã¦ãæªã...ï¼ newStatus: UserStatus! }
ãã£ã¼ã«ãåã¯æ¡å¼µå¯è½ãªå½¢ã«ãã¦ããå¿ è¦ãããã¾ãããå½åã¯ã¨ã¦ãé£ãããªããã¨ãäºæ³ããã¾ãã ã¾ãããã£ã¼ã«ããå¢ãç¶ãããããªããããã¯ããã§ã¢ã³ããã¿ã¼ã³ã«ããã£ã¦ãã¾ã£ã¦ããã¨æããã¾ããã
ãã®æ¹æ³ãåãå ´åããµãã¼ãããã¯ã©ã¤ã¢ã³ãã®ãã¼ã¸ã§ã³ãããã£ã¦ãããããªãã³ã¡ã³ãçã§æ®ãã¦ããã¨è¯ãã§ãããã ãã®ãã¼ã¸ã§ã³ã®å©ç¨è ãããªããªã£ãæã«ãã£ã¼ã«ããåé¤ããã®ã«ä½¿ãã¾ãã
解決ç3. Enum ã使ããªã
ä¸è¨ã®ä¾ã§ããã° Enum ã«ããã«åçã®æ å ±ãè¿ããã£ã¼ã«ããä½ãæ¹æ³ãåãã¾ãã
type User { # å¦çãã©ãã isStudent: Boolean! # è¬å¦ç³»ã¹ãã¼ã¿ã¹ãã©ãã isPharmacyStatus: Boolean! }
ä»å¾æ¡å¼µã®å¯è½æ§ãé«ããã®ã¯ Enum ã¨ãã¦è¡¨ç¾ãããããããã以å¤ã®ãã£ã¼ã«ãã¨ãã¦è¿ãã¦ã¯ã©ã¤ã¢ã³ãå´ã§æ±ºå®ããæ¹æ³ã®æ¹ãå®å ¨ã ã¨æããã¾ãã
å 人ã®ç¥æµãåãã
åé ã§è¿°ã¹ã¾ããéããGraphQL ã«ã¤ãã¦ã®ãã¬ãã¸ããã¼ã ã«ã¯ãªãã£ãããæ©ã¿ã©ããã¯å¤ãã£ãã§ãã ç¹ã«ã¹ãã¼ãè¨è¨ã«ã¤ãã¦ã¯ãµã¼ããµã¤ããã¯ã©ã¤ã¢ã³ããµã¤ãã®ã¨ã³ã¸ãã¢ã交ãã¦è°è«ãéãã¾ããã
ã¹ãã¼ãè¨è¨ã«ã¤ãã¦ã®æéã欲ããã¨æã£ã¦ããã¨ããã§åèã«ãªã£ãããã¥ã¡ã³ãã»æ¸ç±ãç´¹ä»ãã¾ãã
Relay Server Specification*9
Relay Server Specification㯠GraphQL ã®æ¡å¼µä»æ§ã§ããã¹ãã¼ãè¨è¨ã«ã¤ãã¦ããã¤ãã®è¦ç´ãå®ãã¦ãã¾ãã Relay ã«æºæ ããå®è£ ã®ã©ã¤ãã©ãªãå°ãªããªããããåããã¦ããã¦æã¯ãªãã§ããããGraphQL å ¬å¼ãµã¤ã*10
GraphQL ã®ä¸éãã®æ©è½ã«ã¤ãã¦ããã¥ã¡ã³ãããããå®å ¨ã«ç解ããæ°åã«ããã¦ããã¾ããæ©è½ã®èª¬æã¨ãªã£ã¦ãããã使ãæãªã©ãããã«ããã¨ãããããã¾ããããã¯ãå ¬å¼ããã¥ã¡ã³ãã¯èªãã¹ãã§ããGraphQL ã¹ãã¼ãè¨è¨ã«ãã¤ãã*11
ãã¾ãæ¥æ¬èªåããã¦ããªã GraphQL ã®è¨è¨å¨ãã«ã¤ãã¦æ¸ãã¦ããã¾ãã ãã®æ¸ç±ãé å¸ãããé ã«ã¡ããã©æ©ãã§ããã¨ããã ã£ãã¨ã©ã¼ãã³ããªã³ã°ã«ã¤ãã¦ç¹ã«åèã«ããã¦ããã ãã¾ããã
We are hiring
ä»åã¯æ°è¦ã¢ããªã®BFFã«ã¾ã¤ãã話ãããã¦ããã ãã¾ããã
ãã«ãããã¤ã¹ãã¼ã ã§ã¯ãªãªã¼ã¹ãããã°ããã®ã¢ããªãæé·ãããã¹ãä¸ç·ã«éçºã«åå ãã¦ããã仲éãåéä¸ã§ãã
ãã¡ãããµã¼ããµã¤ãã«éãã iOS / Android ã¢ããªã®ã¨ã³ã¸ãã¢ãåéãã¦ãã¾ãã
ãæ°è»½ã«ãåãåãããã ããã
*1:åãã¦ã®GraphQL, Eve Porcello Alex Banks è, å°¾å´ æ²è¶ ããã©ãããã 訳
*2:https://github.com/graphql-java/graphql-java
*3:https://github.com/graphql-java-kickstart/graphql-java-servlet
*4:https://github.com/graphql-java-kickstart/graphql-java-tools
*5:https://github.com/graphql-java-kickstart/graphql-spring-boot
*6:https://github.com/relayjs/relay-examples/tree/master/todo
*7:graphql-spring-boot-starterã®Exceptionãã³ããªã³ã°ããã£ã¡ã便å©ã«ãªã£ã¦ã https://shiraji.hatenablog.com/entry/2019/04/24/080000
*8:When To Use GraphQL Non-Null Fields https://medium.com/@calebmer/when-to-use-graphql-non-null-fields-4059337f6fc8
*9:Relay Server Specification https://relay.dev/docs/en/graphql-server-specification.html
*11:GraphQLã¹ãã¼ãè¨è¨ã¬ã¤ã https://github.com/vvakame/graphql-schema-guide