Java World中的GraphQL简介
許多人認為GraphQL僅適用于前端和JavaScript,它在Java等后端技術中沒有定位,但事實確實如此。
還經常將GraphQL與REST進行比較,但是這種比較是否合理?
首先,讓我開始回答其中最重要的問題。 什么是GraphQL?
如果您查看官方網站,將會看到類似的內容
“ GraphQL是API的查詢語言,并且是服務器端運行時,用于通過使用為數據定義的類型系統來執行查詢。 GraphQL不受任何特定數據庫或存儲引擎的束縛,而是由您現有的代碼和數據支持。”
實際上應該說的是
GraphQL是一個規范,僅此而已。
要記住這一點很重要,因為作為開發人員,我們將使用GraphQL的實現。 一些實現已經或多或少地實現了GraphQL規范中的功能。 有許多語言的實現,例如JavaScript,Java,PHP,Go和其他語言。 每天都有不同語言和現有語言的新實現。
如果您來自Java背景并且有很多REST API,那么您首先會感興趣的是GraphQL與多年來開發的Traditional REST API有何不同。
讓我將其放在一個簡單的博客的上下文中,該博客由博客文章,博客文章的作者組成,并且可以在博客文章中添加評論。
從數據庫的角度來看,這意味著我們有三個表
讓我們假設前端是只讀的,并從Traditional REST API獲取數據,然后將數據呈現給用戶。 如果我們要構建這種傳統的REST API,則可能最終會得到類似以下的代碼
@RestController public class SimpleRestController { @RequestMapping (path= "/authors" ) public List getAllAuthors() { ... } @RequestMapping (path= "/authors/{id}" ) public Author getAuthorById( @PathVariable String id) { ... } @RequestMapping (path= "/posts" ) public List getAllPosts( @RequestParam (value= "author_id" , required = false ) String authId) { ... } @RequestMapping (path= "/comments" ) public List getAllComments( @RequestParam (value= "post_id" , required = false ) String postId) { ... } }因此,在這種情況下,如果我們想顯示包含作者信息和評論的帖子,我們首先需要致電
- /帖子
獲取所有帖子,然后找到我們想要的帖子,查看authorId是什么,然后調用
- / authours / <帖子中的ID>
之后,我們需要致電
- / comments?post_id = <相關帖子的ID>
以獲取該帖子的所有評論。
顯然,這不是最佳方法。 當然,在這種情況下,我們所有人都會做的就是看好我們API的用例,并牢記這一點來優化端點和響應。 也許我們會將評論嵌入帖子,作者信息或類似內容中。 或者,由于某種原因,如果我們認為沒問題,也許我們不會改變任何事情。 無論如何,我們將決定用戶可以呼叫哪些端點,以及他們將獲得什么樣的響應。
確切地說,這是GraphQL的最大區別。 對于GraphQL,通常只有一個端點,例如
- / graphql
該端點將獲取對您的API的所有請求,并發送回所有響應。
起初聽起來有點奇怪。 最簡單的方法是擁有完整的示例代碼。 我將使用一個這樣的示例中的代碼片段。 要獲取完整的代碼,只需點擊以下URL :https://github.com/vladimir-dejanovic/simple-springboot-graphql-mongo-conftalk-demo
要記住的重要一點是,在GraphQL中,一切都始于架構。 如果我們轉到上面的示例,博客文章,GraphQL模式可能看起來像這樣:
type Author { id: ID! name: String! posts: [Post] } type Post { id: ID! title: String! body: String createdBy: Author! comments: [Comment] } type Comment { id: ID! createdBy: Author! belongsTo: Post! text: String } schema { query: Query } type Query { allPosts: [Post] allAuthors: [Author] }我們從定義類型開始,對于將為表創建的POJO,類型幾乎可以是1到1。 首先,我們輸入一個名稱,然后輸入。 字符' ! '具有特殊含義,表示該字段是必填字段。 如果字段具有此字符并且不存在響應,則它將是無效響應,并且GraphQL將不會將響應發送回去,但會發送適當的錯誤。
關于模式要記住的重要一點是,所有請求和響應都將使用模式進行驗證。 如果請求未通過架構驗證,則服務器將不執行任何工作。 另外,如果響應未通過架構驗證,則不會將其發送到客戶端。
如果您選中“作者”類型,您將看到它具有“帖子數組”類型的字段帖子。 另外,Post具有類型為Author和comments的createdBy字段,其類型為Comment的Array。 這些字段在POJO的中不存在
Author.java public class Author { ?private final String id; ???private final String name; .....get/set } Post.java public class Post { private final String id; ???private String authorId; ???private final String title; private final String body; ...get/set }類似的是注釋類型,我稍后會再講。 定義類型之后,我們可以進入GraphQL模式的核心
schema { query: Query }這是我們定義與用戶互動的地方。 我們說用戶可以使用下面定義的Query類型的查詢來讀取數據。
type Query { allPosts: [Post] allAuthors: [Author] }Query是一種特殊類型,因為我們在DB中沒有此數據,這實際上是傳統思維方式中的端點。
如果您是從GitHub鏈接下載代碼,進行編譯并啟動的,則可以轉到http:// localhost:8080 / 。 然后,您將看到名為GraphiQL的漂亮用戶界面。 您可以使用GraphiQL來玩GraphQL API
為了獲得所有帶有ID,標題和正文的帖子,只需將其輸入GraphiQL
query { allPosts { id title body } }響應應如下所示
{ "data" : { "allPosts" : [ { "id" : "59f4c12e7718af0b1e001072" , "title" : "Who is Ed Wong" , "body" : "Edward Wong Hau Pepelu .....” }, . . . . }例如,如果我們對身體不感興趣,我們可以輸入這樣的內容
query { allPosts { id title } }那么響應將是這樣的
{ "data" : { "allPosts" : [ { "id" : "59f4c12e7718af0b1e001072" , "title" : "Who is Ed Wong" , }, . . . . }如您所見,當涉及到GraphQL時,用戶在響應中并不總是獲得相同的預定義字段集。 用戶可以選擇說出哪些字段應該發回,哪些不應該。
允許這樣做的Java代碼不是那么大。 首先,我們需要定義擴展SimpleGraphQLServlet的 Servlet。
public class GraphQLEntryPoint extends SimpleGraphQLServlet { ???public GraphQLEntryPoint(PostRepository postRepository, AuthorRepository authRepository, CommentRepository commentRepository) { super (buildSchema(postRepository, authRepository, commentRepository)); } private static GraphQLSchema buildSchema(PostRepository postRepository, AuthorRepository authRepository, CommentRepository commentRepository) { return SchemaParser .newParser() .file( "schema.graphqls" ) .resolvers( new Query(postRepository, authRepository), new PostResolver(authRepository, commentRepository), new AuthorResolver(postRepository), new CommentResolver(authRepository, postRepository)) .build() .makeExecutableSchema(); } }在這里,我創建模式解析器,該解析器打開我的GraphQL模式文件,之后添加解析器,然后調用build和makeExecutableSchema方法。
這里的重要部分是解析器。 解析器是GraphQL將用于解決用戶請求的類。
首先,最重要的是Query類。 它與模式中的Query類型具有相同的名稱并非偶然。 這就是java GraphQL實現如何從架構中知道哪個類對應于查詢邏輯的。 您可以使用任何喜歡的名稱,只要該類具有相同的名稱即可,但是,這意味著新人們也需要知道該名稱,因此請保持標準,并且對于只讀使用Query。
這是類查詢的代碼
public class Query implements GraphQLRootResolver { private final PostRepository postRepository; private final AuthorRepository authRepo; public List<Post> allPosts() { return postRepository.findAll(); } ???public List<Author> allAuthors() { return authRepo.findAll(); } }它實現了GraphQLRootResolver ,并且您可以看到GraphQL模式中的每一行都有一個方法。
有一個叫allPost方法,該方法返回后的名單,也有方法allAuthors返回作者列表。 為了使我們的API能夠正常工作,這就是所有這些。
如果您返回到GraphiQL并輸入像這樣的輸入
query { allPosts { id title createdBy { name } } }響應將是這樣的
{ "data" : { "allPosts" : [ { "id" : "59f4c12e7718af0b1e001072" , "title" : "Who is Ed Wong" , "createdBy" : { "name" : "Ed Wong” } }, . . . ] }您會突然得到所有數據,而這不是Post pojo的一部分。 正如我們所看到的,Query類并沒有做任何魔術,它只是返回Post類型的普通pojo列表。 那么,createdBy字段的作者信息從何而來?
為此,我們需要查看另一個解析器PostResolver,以使其更加精確,因此讓我們看一下它的代碼
public class PostResolver implements GraphQLResolver<Post> { ???private final AuthorRepository authRepository; private final CommentRepository commentRepository; ???public Author createdBy(Post post) { return authRepository.findOne(post.getAuthorId()); } ???public List<Comment> comments(Post post) { return commentRepository.findByPostId(post.getId()); } }PostResolver實現了GraphQLResolver ,我們不得不說是哪種類型,在這種情況下,是Post類型 。 如您所見,Post中存在模式中的所有字段,但Pojo Post中不存在。 有一個createdBy方法,該方法采用Post類型的參數并返回Author。
此外,還有方法注釋 ,該方法注釋也采用Post類型的參數并返回Comment列表。
這就是全部,這就是我在代碼中使用的GraphQL的java實現如何知道如何解析pojo中不存在的字段的方式。 在pojo的情況下,這非常簡單,如果用戶請求該字段,則只需調用適當的get方法,對于其他字段,必須為實現GraphQLResolver的類型提供解析器,并且需要一種具有正確簽名和返回類型的方法。
如您所見,與我們一直以來創建的傳統REST API相比,使用GraphQL,用戶可以更好地控制他/她將獲取哪些數據以及采用哪種格式。 因此,從用戶的角度來看,這當然具有更好的用戶體驗,因為它具有更大的靈活性。 但是,這也意味著后端需要完成許多工作,因此系統在高負載下仍能正常運行。
在傳統的REST API中,作為開發人員,我們完全控制用戶與端點的交互方式,他們將獲得什么樣的響應以及用戶請求將遵循的路徑在我們的代碼中。 如我們所見,使用GraphQL不再是這種情況。 我們知道的是,用戶將點擊解析器,而不是如何或通過哪個路徑。 因此,優化困難得多。
幸運的是,并不是所有的東西都丟失了,我們仍然可以使用許多舊的技巧來解決這些新的/舊的問題。 例如,如果采用傳統的REST API,解決高性能問題的一種方法是擁有一個帶有端點的控制器,調用服務,然后該服務將承擔繁重的工作。 在此設置中,我們可以緩存所有對服務的調用,并以這種簡單的方式獲得良好的性能。 我們可以用GraphQL做類似的事情,唯一的區別是控制器將調用服務,而不是控制器調用服務。
使用GraphQL,問題可能會更加棘手,但是,可以結合使用一些過去的技巧來使用過去的許多技術。 當然,每天都會出現許多解決問題的新方法。
我只在這里向您展示了如何讀取數據,您當然也可以創建/編輯/修改數據,并使用GraphQL進行更多操作。 與GraphQL在構建API中提供的功能時,我與您分享的內容只是從頭開始。
您需要記住的重要一點是,盡管GraphQL相對較新,但沒有它也可以實現它提供的所有功能。 但是,在這種情況下,您將需要考慮允許用戶做什么以及他們如何將請求發送到您的API。 對于GraphQL,其他人已經考慮過了,而您所要做的就是實現它。
最后,GraphQL API是REST API,這是高級REST API,具有許多功能和特性,因此更加精確。 這就是為什么問自己的問題,這是一件好事,您是否真的需要GraphQL提供的功能,并且會為您的API和為此API構建域的域增加更多的問題或解決方案。 也許GraphQL正是您所需要的,但也許又是舊的傳統REST API所需要的。
資源資源
- 代碼示例https://github.com/vladimir-dejanovic/simple-springboot-graphql-mongo-conftalk-demo
- GraphQL Java實現https://github.com/graphql-java/graphql-java
- 弗拉基米爾·德雅諾維奇(Vladimir Dejanovic)在摩洛哥Devoxx上的Talk GraphQL vs傳統REST API https://www.youtube.com/watch?v=2FH93GaoIto
翻譯自: https://www.javacodegeeks.com/2017/12/gentle-intro-graphql-java-world.html
總結
以上是生活随笔為你收集整理的Java World中的GraphQL简介的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ssh登录linux(ssh 登录lin
- 下一篇: Kafka的Spring Cloud S