Memulai GraphQL dan Spring Boot

1. Perkenalan

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

Artikel ini akan memberikan pengantar untuk menyiapkan server GraphQL menggunakan Spring Boot sehingga dapat ditambahkan ke aplikasi yang sudah ada atau digunakan di aplikasi baru.

2. Apa Itu GraphQL ?

REST API tradisional bekerja dengan konsep Sumber Daya yang dikelola server. Sumber daya ini dapat dimanipulasi dengan beberapa cara standar, mengikuti berbagai verba HTTP. Ini bekerja dengan sangat baik selama API kita sesuai dengan konsep sumber daya, tetapi dengan cepat rusak ketika kita perlu menyimpang darinya.

Ini juga menderita ketika klien membutuhkan data dari berbagai sumber pada waktu yang sama. Misalnya meminta posting blog dan komentar. Biasanya ini diselesaikan dengan meminta klien membuat beberapa permintaan atau dengan meminta server menyediakan data tambahan yang mungkin tidak selalu diperlukan, yang mengarah ke ukuran respons yang lebih besar.

GraphQL menawarkan solusi untuk kedua masalah ini . Ini memungkinkan klien untuk menentukan dengan tepat data apa yang diinginkan, termasuk dari menavigasi sumber daya turunan dalam satu permintaan, dan memungkinkan beberapa kueri dalam satu permintaan.

Ini juga bekerja dengan cara yang jauh lebih RPC, menggunakan kueri dan mutasi bernama alih-alih serangkaian tindakan wajib standar. Ini berfungsi untuk meletakkan kontrol di tempatnya, dengan pengembang API menentukan apa yang mungkin, dan konsumen API apa yang diinginkan.

Misalnya, blog mungkin mengizinkan kueri berikut:

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

Kueri ini akan:

  • meminta sepuluh posting terbaru
  • untuk setiap posting, minta ID, judul, dan kategori
  • untuk setiap posting meminta penulis, mengembalikan ID, nama, dan thumbnail

Dalam REST API tradisional, ini membutuhkan 11 permintaan - 1 untuk postingan dan 10 untuk penulis - atau perlu menyertakan detail penulis di detail postingan.

2.1. Skema GraphQL

Server GraphQL memperlihatkan skema yang menjelaskan API. Skema ini terdiri dari definisi tipe. Setiap tipe memiliki satu atau beberapa bidang, yang masing-masing mengambil nol atau beberapa argumen dan mengembalikan tipe tertentu.

Grafik dibuat dari cara bidang-bidang ini bertumpuk satu sama lain. Perhatikan bahwa grafik tidak perlu asiklik - siklus dapat diterima dengan sempurna - tetapi grafik ini diarahkan. Artinya, klien bisa berpindah dari satu bidang ke turunannya, tetapi tidak bisa secara otomatis kembali ke induk kecuali skema mendefinisikannya secara eksplisit.

Contoh Skema GraphQL untuk blog mungkin berisi definisi berikut, yang menjelaskan Postingan, Penulis postingan, dan kueri root untuk mendapatkan postingan 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! }

The "!" di akhir beberapa nama menunjukkan bahwa ini adalah tipe non-nullable. Semua jenis yang tidak memiliki ini bisa menjadi null dalam respon dari server. Layanan GraphQL menangani ini dengan benar, memungkinkan kami untuk meminta bidang turunan dari tipe nullable dengan aman.

Layanan GraphQL juga mengekspos skema itu sendiri menggunakan sekumpulan bidang standar, memungkinkan klien mana pun untuk menanyakan definisi skema sebelumnya.

Hal ini dapat memungkinkan klien untuk secara otomatis mendeteksi saat skema berubah, dan untuk memungkinkan klien yang secara dinamis beradaptasi dengan cara kerja skema. Salah satu contoh yang sangat berguna dari ini adalah alat GraphiQL - dibahas nanti - yang memungkinkan kita untuk berinteraksi dengan API GraphQL apa pun.

3. Memperkenalkan GraphQL Spring Boot Starter

Spring Boot GraphQL Starter menawarkan cara yang fantastis untuk menjalankan server GraphQL dalam waktu yang sangat singkat . Dikombinasikan dengan pustaka GraphQL Java Tools, kita hanya perlu menulis kode yang diperlukan untuk layanan kita.

3.1. Menyiapkan Layanan

Yang kita butuhkan agar ini berfungsi adalah dependensi yang benar:

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

Spring Boot akan secara otomatis mengambil ini dan mengatur penangan yang sesuai untuk bekerja secara otomatis.

Secara default, ini akan mengekspos Layanan GraphQL di titik akhir / graphql aplikasi kita dan akan menerima permintaan POST yang berisi GraphQL Payload. Endpoint ini dapat disesuaikan di file application.properties kami jika perlu.

3.2. Menulis Skema

Pustaka Alat GraphQL bekerja dengan memproses file Skema GraphQL untuk membangun struktur yang benar dan kemudian menyambungkan kacang khusus ke struktur ini. Starter Spring Boot GraphQL secara otomatis menemukan file skema ini .

File-file ini perlu disimpan dengan ekstensi “. graphqls ”dan bisa ada di mana saja di classpath. Kami juga dapat memiliki file ini sebanyak yang diinginkan, sehingga kami dapat membagi skema menjadi modul sesuai keinginan.

Satu persyaratannya adalah harus ada tepat satu kueri root, dan maksimal satu mutasi root. Ini tidak dapat dibagi menjadi beberapa file, tidak seperti skema lainnya. Ini adalah batasan dari definisi Skema GraphQL itu sendiri, dan bukan dari implementasi Java.

3.3. Root Query Resolver

Kueri root harus memiliki biji khusus yang ditentukan dalam konteks Spring untuk menangani berbagai bidang dalam kueri root ini . Berbeda dengan definisi skema, tidak ada batasan bahwa hanya ada satu kacang Spring untuk bidang kueri root.

Satu-satunya persyaratan adalah bahwa kacang menerapkan GraphQLQueryResolver dan bahwa setiap bidang di kueri root dari skema memiliki metode 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 metode harus salah satu dari yang berikut, dalam urutan ini:

  1. adalah - hanya jika kolom berjenis Boolean
  2. Dapatkan

Metode ini harus memiliki parameter yang sesuai dengan parameter apa pun dalam skema GraphQL, dan secara opsional dapat mengambil parameter akhir tipe 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 merevolusi cara API Web dikembangkan.

Kombinasi dari Spring Boot GraphQL Starter dan library GraphQL Java Tools membuatnya sangat mudah untuk menambahkan teknologi ini ke aplikasi Spring Boot baru atau yang sudah ada.

Potongan kode dapat ditemukan di GitHub.