7

How to generate Kotlin DSL Client by GraphQL schema

 2 years ago
source link: https://blog.kotlin-academy.com/how-to-generate-kotlin-dsl-client-by-graphql-schema-707fd0c55284
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

How to generate Kotlin DSL Client by GraphQL schema

Introducing the Kobby Plugin — a code generator of Kotlin GraphQL Client

«Kotlin» and «GraphQL» mean the same thing to you!

On the one hand, the GraphQL schema uniquely defines the data model and the available operations of the service that implements it. On the other hand, Kotlin provides amazing opportunities for creating your own domain-specific languages (DSL). This way, you can write a domain-specific language to interact with your GraphQL service according to the published GraphQL schema. But writing such a code manually is Sisyphean labor. This is where the Kobby Plugin comes in, which can parse your service’s GraphQL schema and generate a client DSL for interaction. Let’s try it out!

The source code for the examples in this article is available in Kobby Gradle Tutorial and Kobby Maven Tutorial.

What will we get in the end?

If you are too lazy to read the whole article…

Query

Mutation

Subscription

Kobby Plugin setup

In the beginning was the Word…

But we will start with a schema of our service. By default, the Kobby Plugin looks for a GraphQL schema in files with graphqls extensions in the project resources. For the sake of simplicity, we will place our schema in a single file named cinema.graphqls:

This simple schema will allow us to try out all kinds of GraphQL operations — queries, mutations, and subscriptions.

Next, we have to configure the plugin. For Gradle projects it looks like this:

For Maven projects, this does not look so elegant:

There are explicit and implicit ways to configure the plugin. We used the implicit way by adding dependencies to the Jackson and Ktor libraries. The Kobby Plugin not only scans resources for schema files but also analyzes project dependencies. If Jackson is present in the dependencies, then the plugin adds Jackson annotations to the generated DTO classes to ensure JSON serialization. If Ktor is present in the project dependencies, then the plugin generates a default DSL adapter. We’ll talk about adapters in the next section.

Instantiate DSL Context

We have configured our plugin. Now we have to generate a DSL according to our schema. Run gradle build for Gradle or mvn compile for Maven and the plugin will find the cinema.graphqls file and generate a DSL based on it:

The plugin will create the cinema.kt file with the cinemaContextOf builder function that instantiates the CinemaContext interface — the entry point of the generated DSL:

We have to pass an instance of the CinemaAdapter interface to the builder function to create the context. What is an adapter? The DSL context, generated by the Kobby Plugin, knows nothing about the transport layer and GraphQL communication protocol. The context implementation just builds the query and passes it to the adapter. And the adapter has to do all the dirty work — send the query to the server, and receive and deserialize the response. You can write your own adapter implementation or use the default adapter generated by the plugin.

We will use the default adapter. It uses Ktor to execute GraphQL queries and mutations over HTTP and to establish subscription sessions over WebSocket:

Executing Queries

We are ready to execute our first query. Let’s try to find a film with actors by ID. In GraphQL, this query looks like this:

In Kotlin, it looks like this:

The context.query is suspending function, and does not block the current thread. What do we get as a result of executing the query? In GraphQL, the result is JSON that looks like this:

To navigate through the result, the plugin generates “entity” interfaces that look like this:

The context.query function returns an instance of the Query entity, so the navigation through the result looks like this:

Executing Mutations

Let’s create a new movie. In GraphQL, the mutation looks like this:

And we’ll get the result as a JSON that looks like this:

In Kotlin, this mutation looks like this:

The context.mutation function returns an instance of the Mutation entity interface and also is suspending function. So, our mutation does not block the current thread.

Establishing Subscriptions

Let’s subscribe for new films notifications. In GraphQL, the subscription looks like this:

With this subscription, we will receive JSON messages that look like this:

The semantics of the subscription operation in Kotlin is different from the semantics of the query and mutation operations. While the query and mutation just send a request and receive a response, the subscription creates a long-lived session to listen to incoming messages. So, we have to create an asynchronous listener to receive incoming messages:

Don’t worry, we are not blocking the thread in an infinite loop, because the subscribe function and the receive function are suspending functions.

The lifetime of the subscription session is the same as the execution time of the subscribe function. When we enter the subscribe function, a session is created, and when we exit it, the session is destroyed.

The receive function returns an instance of the Subscription entity interface for each incoming message.

What have I not covered in this article?

And most importantly, I didn’t talk about how to use Kotlin extension functions to turn the generated DSL into a rich domain model on steroids. Perhaps I will cover this in future articles.

1*vjGp5fHBUq3Gzf8MiBUoiQ.png?q=20
how-to-generate-kotlin-dsl-client-by-graphql-schema-707fd0c55284

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK