GraphQL標準提供了多種主流程式設計語言的實現,本文介紹其中的Java實現graphql-java。所謂GraphQL標準的實現,就是一個GraphQL伺服器的實現,通過一組軟體組件,以支援要求的解析、驗證和執行GraphQL查詢。
graphql-java就是GraphQL標準的Java語言實現之一,當前最新版本是8.0。在Maven項目中配置如下:
<dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-java</artifactId> <version>8.0</version></dependency>
本文主要介紹graphql-java的核心的GraphQL Schema定義。
1. GraphQL Schema
就類似於資料庫伺服器要響應使用者對資料庫的訪問,首先要定義資料庫Schema一樣。GraphQL伺服器要是響應使用者對REST資料的查詢請求,也要定義一個描述服務資料的結構,就是GraphQL Schema。
為了定義GraphQL Schema,graphql-java提供了如下兩種定義的方式: 代碼中編程定義動態GraphQL Schema
GraphQLObjectType fooType = newObject() .name("Ci") .field(newFieldDefinition() .name("product_number") .type(GraphQLString)) .build();
如代碼所示,運行中建立了新類型Ci,並為其定義了屬性product_number,該屬性的類型為Scalar的GraphQLString。 使用SDL (Schema Definition Language)在獨立的資源檔中定義靜態GraphQL Schema
建立src/main/resources/myschema.graphqls檔案,定義類型Ci如下:
type Ci { product_number: String}
這裡,為了樣本,兩種方式定義的GraphQL Schema完全等價。
實際上定義完整的GraphQL Schema還有具體的文法,請參考建立GraphQL Schema。
2. DataFetcher/TypeResolver
GraphQL Schema中的每個欄位類型,都關聯一個DataFetcher(在Node.js實現或Python實現中被稱為resolver函數),用以為GraphQL Schema中的欄位類型擷取資料。資料來源可以是資料庫或REST服務,並將資料表示為Java POJO對象。DataFetcher對象可以直接讀取Java POJO對象的屬性,再使用Jackson或GSON轉換為JSON格式的資料。
DataFetcher pnDataFetcher = new DataFetcher() { @Override public Object get(DataFetchingEnvironment environment) { Map response = fetchPnFromDatabase(environment.getArgument("product_number"));List<GraphQLError> errors = ((List)response.get("errors")).stream().map(MyMapGraphQLError::new).collect(Collectors.toList());return new DataFetcherResult(response.get("data"), errors); }};
GraphQLObjectType objectType = newObject() .name("Ci") .field(newFieldDefinition() .name("product_number") .type(GraphQLString) .dataFetcher(pnDataFetcher)) .build();
注意,這裡的dataFetcher()方法的使用。
與DataFetcher擷取資料的作用相似,TypeResolver提供了更強大的功能,能夠根據擷取的資料轉換為對應的Schema中定義的欄位類型,這對面向介面的編程有很大的意義。在一個擷取資料的方法中,定義返回的類型是介面,而實際返回可能是實現該介面的任何對象,根據擷取的資料轉換為對應的對象,返回即可。
3. Runtime Wiring
建立完畢GraphQL Schema還只是開始,還要將其應用在GraphQL伺服器上。這就類似於定義資料庫結構的SDL指令碼已經準備就緒,但是真正在資料庫伺服器上執行後才生效。
在GraphQL伺服器上真正執行GraphQL Schema使其運行起來,是通過Runtime Wiring,這樣才能夠得到一個可執行檔GraphQL Schema。典型代碼如下:
File schemaFile = loadSchema("myschema.graphqls");SchemaParser schemaParser = new SchemaParser();TypeDefinitionRegistry typeRegistry = schemaParser.parse(schemaFile);RuntimeWiring wiring = RuntimeWiring.newRuntimeWiring()....build();SchemaGenerator schemaGenerator = new SchemaGenerator();GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeRegistry, wiring);
4. GraphQL服務
已經有了完整的可響應請求的GraphQL Schema,現在就運行起來吧。
GraphQL build = GraphQL.newGraphQL(graphQLSchema).build();
5. Execution請求GraphQL服務
1) 請求參數
在請求應用中,可以直接將請求參數以字串的形式傳遞給GraphQL服務,如下所示:
ExecutionResult executionResult = build.execute("query { hero { name } }");
也可以先構建ExecutionInput對象(適合複雜的請求參數),然後再傳遞給GraphQL服務,如下所示:
ExecutionInput executionInput = ExecutionInput.newExecutionInput().query("query { hero { name } }") .build();ExecutionResult executionResult = build.execute(executionInput);
2)處理結果
GraphQL服務的響應結果在data中,如果出錯則在
executionResult.getData().toString();List<GraphQLError> errors = executionResult.getErrors();
如果要將得到的響應資料序列化為JSON格式,為了完全相容GraphQL標準,建議首先將響應結果進行正常化轉換,然後再調用Jackson或GSON等JSON庫進行JSON格式轉換。
Map<String, Object> toSpecificationResult = executionResult.toSpecification();doWithJson(toSpecificationResult);
這裡只討論了同步的請求,非同步請求可以參考其文檔。
參考連結:
https://github.com/graphql-java/graphql-java
http://graphql-java.readthedocs.io/en/latest/