Bermula dengan GraphQL dan Spring Boot

1. Pengenalan

GraphQL adalah konsep yang agak baru dari Facebook yang ditagih sebagai alternatif REST untuk Web API.

Artikel ini akan memberikan pengenalan untuk mengatur pelayan GraphQL menggunakan Spring Boot sehingga dapat ditambahkan ke aplikasi yang ada atau digunakan dalam yang baru.

2. Apa itu GraphQL ?

API REST tradisional berfungsi dengan konsep Sumber yang dikendalikan oleh pelayan. Sumber-sumber ini dapat dimanipulasi dengan beberapa cara standard, mengikuti pelbagai kata kerja HTTP. Ini berfungsi dengan baik selagi API kami sesuai dengan konsep sumber, tetapi cepat hancur apabila kita perlu menyimpangnya.

Ini juga menderita apabila pelanggan memerlukan data dari pelbagai sumber pada masa yang sama. Contohnya, meminta catatan blog dan komen. Biasanya ini diselesaikan dengan meminta pelanggan membuat banyak permintaan atau dengan menyediakan pelayan data tambahan yang mungkin tidak selalu diperlukan, yang menyebabkan ukuran respons lebih besar.

GraphQL menawarkan penyelesaian untuk kedua-dua masalah ini . Ini memungkinkan klien untuk menentukan dengan tepat data apa yang diinginkan, termasuk dari menavigasi sumber daya anak dalam satu permintaan, dan memungkinkan untuk beberapa pertanyaan dalam satu permintaan.

Ia juga berfungsi dengan cara RPC yang lebih banyak, menggunakan pertanyaan dan mutasi bernama dan bukan set tindakan wajib standard. Ini berfungsi untuk meletakkan kawalan di mana ia berada, dengan pengembang API menentukan apa yang mungkin, dan pengguna API apa yang diinginkan.

Sebagai contoh, blog mungkin membenarkan pertanyaan berikut:

query { recentPosts(count: 10, offset: 0) { id title category author { id name thumbnail } } }

Pertanyaan ini akan:

  • minta sepuluh catatan terbaru
  • untuk setiap siaran, minta ID, tajuk, dan kategori
  • untuk setiap catatan meminta pengarang, mengembalikan ID, nama, dan lakaran kecil

Dalam REST API tradisional, ini memerlukan 11 permintaan - 1 untuk siaran dan 10 untuk pengarang - atau perlu memasukkan butiran pengarang dalam perincian catatan.

2.1. Skema GraphQL

Pelayan GraphQL memaparkan skema yang menggambarkan API. Skema ini terdiri daripada definisi jenis. Setiap jenis mempunyai satu atau lebih medan, yang masing-masing memerlukan sifar atau lebih argumen dan mengembalikan jenis tertentu.

Grafik dibuat berdasarkan cara bidang ini bersarang antara satu sama lain. Perhatikan bahawa tidak perlu grafik menjadi asiklik - kitaran boleh diterima dengan sempurna - tetapi diarahkan. Maksudnya, pelanggan boleh mendapatkan dari satu bidang ke anak-anaknya, tetapi tidak dapat kembali ke ibu bapa secara automatik kecuali skema menentukannya secara jelas.

Contoh Skema GraphQL untuk blog mungkin mengandungi definisi berikut, yang menerangkan Post, Author of post dan root query untuk mendapatkan posting terbaru di blog.

type Post { id: ID! title: String! text: String! category: String author: Author! } type Author { id: ID! name: String! thumbnail: String posts: [Post]! } # The Root Query for the application type Query { recentPosts(count: Int, offset: Int): [Post]! } # The Root Mutation for the application type Mutation { writePost(title: String!, text: String!, category: String) : Post! }

"!" pada akhir beberapa nama menunjukkan bahawa ini adalah jenis yang tidak boleh dibatalkan. Apa-apa jenis yang tidak mempunyai ini boleh menjadi batal dalam tindak balas dari pelayan. Perkhidmatan GraphQL menangani ini dengan betul, yang membolehkan kami meminta bidang kanak-kanak jenis nullable dengan selamat.

Perkhidmatan GraphQL juga memperlihatkan skema itu sendiri menggunakan set bidang yang standard, yang memungkinkan setiap pelanggan untuk meminta definisi skema terlebih dahulu.

Ini membolehkan klien mengesan secara automatik ketika skema berubah, dan memungkinkan klien yang secara dinamis menyesuaikan diri dengan cara skema berfungsi. Salah satu contoh yang sangat berguna ialah alat GraphiQL - dibincangkan kemudian - yang membolehkan kita berinteraksi dengan API GraphQL mana pun.

3. Memperkenalkan GraphQL Spring Boot Starter

Spring Boot GraphQL Starter menawarkan cara yang hebat untuk menjalankan pelayan GraphQL dalam masa yang sangat singkat . Digabungkan dengan pustaka GraphQL Java Tools, kami hanya perlu menulis kod yang diperlukan untuk perkhidmatan kami.

3.1. Menetapkan Perkhidmatan

Yang kita perlukan untuk berfungsi adalah kebergantungan yang betul:

 com.graphql-java graphql-spring-boot-starter 5.0.2   com.graphql-java graphql-java-tools 5.2.4 

Spring Boot secara automatik akan mengambilnya dan menyediakan pengendali yang sesuai untuk berfungsi secara automatik.

Secara lalai, ini akan memaparkan Perkhidmatan GraphQL pada titik akhir / graphql aplikasi kami dan akan menerima permintaan POST yang mengandungi Beban GraphQL. Titik akhir ini boleh disesuaikan dalam fail application.properties kami jika perlu.

3.2. Menulis Skema

Perpustakaan GraphQL Tools berfungsi dengan memproses fail GraphQL Schema untuk membina struktur yang betul dan kemudian memasang kabel khas ke struktur ini. Permulaan Spring Boot GraphQL secara automatik menemui fail skema ini .

Fail-fail ini perlu disimpan dengan pelanjutan “. graphqls ”dan boleh hadir di mana sahaja di lorong kelas. Kami juga dapat memiliki seberapa banyak fail ini seperti yang diinginkan, sehingga kami dapat membagi skema menjadi modul seperti yang diinginkan.

Satu syaratnya ialah mesti ada satu pertanyaan akar, dan hingga satu mutasi root. Ini tidak boleh dibahagi-bahagikan di antara fail, tidak seperti skema yang lain. Ini adalah batasan definisi Skema GraphQL itu sendiri, dan bukan pelaksanaan Java.

3.3. Penyelesaian Permintaan Root

Permintaan akar perlu mempunyai kacang khas yang ditentukan dalam konteks Spring untuk menangani pelbagai bidang dalam pertanyaan akar ini . Tidak seperti definisi skema, tidak ada batasan bahawa hanya terdapat satu kacang Spring untuk medan pertanyaan akar.

Satu-satunya syarat adalah bahawa kacang menerapkan GraphQLQueryResolver dan bahawa setiap bidang dalam pertanyaan akar dari skema mempunyai kaedah di salah satu kelas ini dengan nama yang sama.

public class Query implements GraphQLQueryResolver { private PostDao postDao; public List getRecentPosts(int count, int offset) { return postsDao.getRecentPosts(count, offset); } }

Nama kaedah mestilah salah satu daripada yang berikut, dalam urutan ini:

  1. adalah - hanya jika bidangnya adalah jenis Boolean
  2. dapatkan

Kaedah ini mesti mempunyai parameter yang sesuai dengan parameter apa pun dalam skema GraphQL, dan secara opsional dapat mengambil parameter akhir dari jenis DataFetchingEnvironment.

The method must also return the correct return type for the type in the GraphQL scheme, as we are about to see. Any simple types – String, Int, List, etc. – can be used with the equivalent Java types, and the system just maps them automatically.

The above defined the method getRecentPosts which will be used to handle any GraphQL queries for the recentPosts field in the schema defined earlier.

3.4. Using Beans to Represent Types

Every complex type in the GraphQL server is represented by a Java bean – whether loaded from the root query or from anywhere else in the structure. The same Java class must always represent the same GraphQL type, but the name of the class is not necessary.

Fields inside the Java bean will directly map onto fields in the GraphQL response based on the name of the field.

public class Post { private String id; private String title; private String category; private String authorId; }

Any fields or methods on the Java bean that do not map on to the GraphQL schema will be ignored, but will not cause problems. This is important for field resolvers to work.

For example, the field authorId here does not correspond to anything in our schema we defined earlier, but it will be available to use for the next step.

3.5. Field Resolvers for Complex Values

Sometimes, the value of a field is non-trivial to load. This might involve database lookups, complex calculations, or anything else. GraphQL Tools has a concept of a field resolver that is used for this purpose. These are Spring beans that can provide values in place of the data bean.

The field resolver is any bean in the Spring Context that has the same name as the data bean, with the suffix Resolver, and that implements the GraphQLResolver interface. Methods on the field resolver bean follow all of the same rules as on the data bean but are also provided the data bean itself as a first parameter.

If a field resolver and the data bean both have methods for the same GraphQL field then the field resolver will take precedence.

public class PostResolver implements GraphQLResolver { private AuthorDao authorDao; public Author getAuthor(Post post) { return authorDao.getAuthorById(post.getAuthorId()); } }

The fact that these field resolvers are loaded from the Spring context is important. This allows them to work with any other Spring managed beans – e.g., DAOs.

Importantly, if the client does not request a field, then the GraphQL Server will never do the work to retrieve it. This means that if a client retrieves a Post and does not ask for the Author, then the getAuthor() method above will never be executed, and the DAO call will never be made.

3.6. Nullable Values

The GraphQL Schema has the concept that some types are nullable and others are not.

This can be handled in the Java code by directly using null values, but equally, the new Optional type from Java 8 can be used directly here for nullable types, and the system will do the correct thing with the values.

This is very useful as it means that our Java code is more obviously the same as the GraphQL schema from the method definitions.

3.7. Mutations

So far, everything that we have done has been about retrieving data from the server. GraphQL also has the ability to update the data stored on the server, by means of mutations.

From the point of view of the code, there is no reason that a Query can't change data on the server. We could easily write query resolvers that accept arguments, save new data and return those changes. Doing this will cause surprising side effects for the API clients, and is considered bad practice.

Instead, Mutations should be used to inform the client that this will cause a change to the data being stored.

Mutations are defined in the Java code by using classes that implement GraphQLMutationResolver instead of GraphQLQueryResolver.

Otherwise, all of the same rules apply as for queries. The return value from a Mutation field is then treated exactly the same as from a Query field, allowing nested values to be retrieved as well.

public class Mutation implements GraphQLMutationResolver { private PostDao postDao; public Post writePost(String title, String text, String category) { return postDao.savePost(title, text, category); } }

4. Introducing GraphiQL

GraphQL also has a companion tool called GraphiQL. This is a UI that is able to communicate with any GraphQL Server and execute queries and mutations against it. A downloadable version of it exists as an Electron app and can be retrieved from here.

It is also possible to include the web-based version of GraphiQL in our application automatically, by adding the GraphiQL Spring Boot Starter dependency:

 com.graphql-java graphiql-spring-boot-starter 5.0.2  

This will only work if we are hosting our GraphQL API on the default endpoint of /graphql though, so the standalone application will be needed if that is not the case.

5. Summary

GraphQL adalah teknologi baru yang sangat menarik yang berpotensi merevolusikan cara API Web dibangunkan.

Kombinasi perpustakaan Spring Boot GraphQL Starter dan GraphQL Java Tools menjadikannya sangat mudah untuk menambahkan teknologi ini ke aplikasi Spring Boot yang baru atau yang ada.

Coretan kod boleh didapati di GitHub.