概要
- JavaでGraphQLサーバーを構築した際のファイルアップロード処理の実装例
- GraphQLクライアント側の説明は割愛
構成
- Java 11
- SpringBoot 2.3.4
- gradle 7.4.1
- GraphQL For Java
- graphql-spring-boot-starter 7.1.0 ref. 1
- io.github.kobylynskyi.graphql.codegen 5.5.0
※. 最新のSpringBootの構成ではないため注意!
実装方法
ポイント
- schema定義について
- GraphQLのschema定義ではアップロードするファイルをUploadで定義する
- Uploadは、Java側で任意のクラスにマッピングするため、scalarに記載する
- Java側について
- schema定義のUploadをjavax.servlet.http.Partで受け取る
詳細
- 全ソースはここからどうぞ
① 各種設定ファイルを準備
- Spring Initializrのベースになるプロジェクトを作成
- build.gradleを以下のように記載
-
build.gradle
plugins { id 'org.springframework.boot' version '2.3.4.RELEASE' id 'io.spring.dependency-management' version '1.0.15.RELEASE' id 'io.github.kobylynskyi.graphql.codegen' version '5.5.0' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '11' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter' testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation 'com.graphql-java-kickstart:graphql-spring-boot-starter:7.1.0' testImplementation 'com.graphql-java-kickstart:graphql-spring-boot-starter:7.1.0' } tasks.named('test') { useJUnitPlatform() }
-
- URLなどはデフォルト設定のまま利用するので、application.propertiesは変更しなくてOK ref. 2
② ファイルアップロードのschema定義を作成
- 以下のようにする ref. 3
-
graphql/schema.graphqls
""" アップロードファイル """ scalar Upload # 今回、Queryは使用しないが、書かないとアプリケーション起動時に怒られるため記載 type Query {} type Mutation { """ ファイルをアップロードする """ upload( """ ファイル """ file: Upload ): Boolean }
-
③ schema定義からJavaのインターフェース・クラスを生成する
- 手で作成してもよいですが今回はgraphql-java-codegenで生成します。ref. 4
- build.gradleに以下のように設定を追加
-
build.gradle
// 末尾に追記 // GraphQL schema定義からのJavaコード生成 apply from: 'gradle/graphql-java-codegen.gradle'
-
- 設定ファイルgraphql-java-codegen.gradleを作成し、schema.graphqlsにおいてscalarに定義したUploadをMappingする型などを設定 ref. 3,ref. 5
-
gradle/graphql-java-codegen.gradle
apply plugin: "io.github.kobylynskyi.graphql.codegen" compileJava.options.encoding = 'UTF-8' graphqlCodegen { // all config options: // https://github.com/kobylynskyi/graphql-java-codegen/blob/master/docs/codegen-options.md outputDir = new File("$buildDir/generated") graphqlSchemas.includePattern = "schema\\.graphqls" apiPackageName = "com.example.demographqlfileupload.graphql.resolvers" // 通常、modelが存在しないことはほぼあり得ないが、本サンプルでは存在しないため指定しない //modelPackageName = "com.example.demographqlfileupload.graphql.model" modelNameSuffix = "TO" // scalarのマッピング対象クラス customTypesMapping = [ Upload: "javax.servlet.http.Part" ] parentInterfaces { queryResolver = "graphql.kickstart.tools.GraphQLQueryResolver" mutationResolver = "graphql.kickstart.tools.GraphQLMutationResolver" } }
-
- 設定ファイルを元に以下のgradleコマンドを実行しファイルを生成する。
gradle graphqlCodegen -Dfile.encoding=utf-8
-
$buildDir/generated
配下にファイルが生成されているのでプロジェクトの任意のフォルダに移動。今回は以下に移動した。/demographqlfileupload/src/main/java/com/example/demographqlfileupload/graphql/resolvers
- 以下のようなファイルが生成されるので、プロジェクトにコピー
-
UploadMutationResolver.java
package com.example.demographqlfileupload.graphql.resolvers; /** * ファイルをアップロードする */ @javax.annotation.processing.Generated( value = "com.kobylynskyi.graphql.codegen.GraphQLCodegen", date = "2022-10-25T19:12:40+0900" ) public interface UploadMutationResolver extends graphql.kickstart.tools.GraphQLMutationResolver { /** * ファイルをアップロードする */ Boolean upload(javax.servlet.http.Part file) throws Exception; }
-
④ scalarからの変換を明示
- 以下のようなクラスを作成 ref. 6
-
ExtendedScalarConfiguration.java
package com.example.demographqlfileupload.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import graphql.kickstart.servlet.apollo.ApolloScalars; import graphql.schema.GraphQLScalarType; @Configuration public class ExtendedScalarConfiguration { @Bean public GraphQLScalarType uploadScalar() { return ApolloScalars.Upload; } }
-
⑤ ③で生成したinterfaceを使ってResolverクラスを作成
- 以下のようなクラスを作成
-
UploadResolver
package com.example.demographqlfileupload.resolver; import javax.servlet.http.Part; import org.springframework.stereotype.Component; import com.example.demographqlfileupload.graphql.resolvers.QueryResolver; import com.example.demographqlfileupload.graphql.resolvers.UploadMutationResolver; @Component public class UploadResolver implements UploadMutationResolver, QueryResolver { @Override public Boolean upload(Part file) throws Exception { if (Objects.isNull(file)) { return false; } System.out.println(file.getSubmittedFileName()); return null; } }
-
⑥ 適宜ちゃんとファイル処理できるようにして完成!
動作確認
- いろいろGraphQLクライアントはあると思いますが、私はPostmanで統一したいのでPostmanを使っています。
- ファイルアップロードについては癖があるので注意
Postmanでの手順 ref. 7
- GraphQLではなくform-dataを選択
- KEY/VALUEに以下のように設定
-
operations
/{"query":"mutation upload($file: Upload){upload(file: $file)}","variables":{"file":null}}
-
map
/{"0":["variables.file"]}
-
0
/ 任意のファイル ※. ここは形式をTextではなくFileに変更
-
- 実行!ファイル情報も取得できているしよさそう
まとめ
- GraphQLのschema定義scalarでUploadを指定する
- Java側ではschema定義のUploadをjavax.servlet.http.Partで受け取れるようマッピングしてやる
- SpringBootのバージョンによって取得するGraphQLのDependenciesが変わっているので注意!!
その他
- GraphQLはアップロードはサポートしていますがダウンロードに使うのは想定されていないようです。自前で実装しましょう。
- Springならコントローラーを追加して通常のHttpRequestとして処理すればOK
- Spring Boot 2.7 以降はSpring InitializrでSpring for GraphQLというDependenciesを利用することができます。提供されるパッケージが変わるため本稿の実装はそのままは使えないです。
参考情報
引用部
- ref. 1 GraphQL Java Kickstart
- ref. 2 Enable GraphQL Servlet
- ref. 3 How to upload files with graphql-java
- ref. 4 kobylynskyi/graphql-java-codegen
- ref. 5 graphql-java-codegen/docs/codegen-options.md
- ref. 6 Custom Scalar実装(GraphQL-SpringBoot)
- ref. 7 Is there any way to upload files via postman into a GraphQL API?
- ref. 8 GraphQL with spring-boot-starter-graphql
- ref. 9 Spring for GraphQL Documentation
- ref. 10 Getting started with Spring for GraphQL